//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Diagnostics.Contracts;
using Microsoft.Boogie;

namespace Microsoft.Dafny {
    public class ResolutionErrorReporter {
        public int ErrorCount = 0;

        /// <summary>
        /// This method is virtual, because it is overridden in the VSX plug-in for Dafny.
        /// </summary>
        public virtual void Error(IToken tok, string msg, params object[] args) {
            Contract.Requires(tok != null);
            Contract.Requires(msg != null);
            ConsoleColor col = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("{0}({1},{2}): Error: {3}",
                tok.filename, tok.line, tok.col - 1,
                string.Format(msg, args));
            Console.ForegroundColor = col;
            ErrorCount++;
        }
        public void Error(Declaration d, string msg, params object[] args) {
            Contract.Requires(d != null);
            Contract.Requires(msg != null);
            Error(d.tok, msg, args);
        }
        public void Error(Statement s, string msg, params object[] args) {
            Contract.Requires(s != null);
            Contract.Requires(msg != null);
            Error(s.Tok, msg, args);
        }
        public void Error(NonglobalVariable v, string msg, params object[] args) {
            Contract.Requires(v != null);
            Contract.Requires(msg != null);
            Error(v.tok, msg, args);
        }
        public void Error(Expression e, string msg, params object[] args) {
            Contract.Requires(e != null);
            Contract.Requires(msg != null);
            Error(e.tok, msg, args);
        }
        public void Warning(IToken tok, string msg, params object[] args) {
            Contract.Requires(tok != null);
            Contract.Requires(msg != null);
            ConsoleColor col = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("{0}({1},{2}): Warning: {3}",
                tok.filename, tok.line, tok.col - 1,
                string.Format(msg, args));
            Console.ForegroundColor = col;
        }
    }

    public class Resolver : ResolutionErrorReporter {
        readonly BuiltIns builtIns;

        //Dictionary<string/*!*/,TopLevelDecl/*!*/>/*!*/ classes;  // can map to AmbiguousTopLevelDecl
        //Dictionary<string, ModuleDecl> importedNames; // the imported modules, as a map.
        ModuleSignature moduleInfo = null;

        class AmbiguousTopLevelDecl : TopLevelDecl  // only used with "classes"
        {
            readonly TopLevelDecl A;
            readonly TopLevelDecl B;
            public AmbiguousTopLevelDecl(ModuleDefinition m, TopLevelDecl a, TopLevelDecl b)
                : base(a.tok, a.Name + "/" + b.Name, m, new List<TypeParameter>(), null) {
                A = a;
                B = b;
            }
            public string ModuleNames() {
                string nm;
                if (A is AmbiguousTopLevelDecl) {
                    nm = ((AmbiguousTopLevelDecl)A).ModuleNames();
                } else {
                    nm = A.Module.Name;
                }
                if (B is AmbiguousTopLevelDecl) {
                    nm += ", " + ((AmbiguousTopLevelDecl)B).ModuleNames();
                } else {
                    nm += ", " + B.Module.Name;
                }
                return nm;
            }
        }

        class AmbiguousMemberDecl : MemberDecl  // only used with "classes"
        {
            readonly MemberDecl A;
            readonly MemberDecl B;
            public AmbiguousMemberDecl(ModuleDefinition m, MemberDecl a, MemberDecl b)
                : base(a.tok, a.Name + "/" + b.Name, a.IsStatic, a.IsGhost, null) {
                A = a;
                B = b;
            }
            public string ModuleNames() {
                string nm;
                if (A is AmbiguousMemberDecl) {
                    nm = ((AmbiguousMemberDecl)A).ModuleNames();
                } else {
                    nm = A.EnclosingClass.Module.Name;
                }
                if (B is AmbiguousMemberDecl) {
                    nm += ", " + ((AmbiguousMemberDecl)B).ModuleNames();
                } else {
                    nm += ", " + B.EnclosingClass.Module.Name;
                }
                return nm;
            }
        }
        //Dictionary<string/*!*/, Tuple<DatatypeCtor, bool>> allDatatypeCtors;

        readonly Dictionary<ClassDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>/*!*/ classMembers = new Dictionary<ClassDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>();
        readonly Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>/*!*/ datatypeMembers = new Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, MemberDecl/*!*/>/*!*/>();
        readonly Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, DatatypeCtor/*!*/>/*!*/>/*!*/ datatypeCtors = new Dictionary<DatatypeDecl/*!*/, Dictionary<string/*!*/, DatatypeCtor/*!*/>/*!*/>();
        readonly Graph<ModuleDecl/*!*/>/*!*/ dependencies = new Graph<ModuleDecl/*!*/>();
        private ModuleSignature systemNameInfo = null;
        private bool useCompileSignatures = false;

        public Resolver(Program prog) {
            Contract.Requires(prog != null);
            builtIns = prog.BuiltIns;
        }

        [ContractInvariantMethod]
        void ObjectInvariant() {
            Contract.Invariant(builtIns != null);
            Contract.Invariant(cce.NonNullElements(dependencies));
            Contract.Invariant(cce.NonNullDictionaryAndValues(classMembers) && Contract.ForAll(classMembers.Values, v => cce.NonNullDictionaryAndValues(v)));
            Contract.Invariant(cce.NonNullDictionaryAndValues(datatypeCtors) && Contract.ForAll(datatypeCtors.Values, v => cce.NonNullDictionaryAndValues(v)));
        }

        public void ResolveProgram(Program prog) {
            Contract.Requires(prog != null);
            var bindings = new ModuleBindings(null);
            var b = BindModuleNames(prog.DefaultModuleDef, bindings);
            bindings.BindName("_module", prog.DefaultModule, b);
            if (ErrorCount > 0) { return; } // if there were errors, then the implict ModuleBindings data structure invariant
            // is violated, so Processing dependencies will not succeed.
            ProcessDependencies(prog.DefaultModule, b, dependencies);
            // check for cycles in the import graph
            List<ModuleDecl> cycle = dependencies.TryFindCycle();
            if (cycle != null) {
                var cy = Util.Comma(" -> ", cycle, m => m.Name);
                Error(cycle[0], "module definition contains a cycle (note: parent modules implicitly depend on submodules): {0}", cy);
            }
            if (ErrorCount > 0) { return; } // give up on trying to resolve anything else

            // fill in module heights
            List<ModuleDecl> sortedDecls = dependencies.TopologicallySortedComponents();
            int h = 0;
            foreach (ModuleDecl m in sortedDecls) {
                m.Height = h;
                if (m is LiteralModuleDecl) {
                    var mdef = ((LiteralModuleDecl)m).ModuleDef;
                    mdef.Height = h;
                    prog.Modules.Add(mdef);
                }
                h++;
            }

            var refinementTransformer = new RefinementTransformer(this, prog);

            IRewriter rewriter = new AutoContractsRewriter();
            systemNameInfo = RegisterTopLevelDecls(prog.BuiltIns.SystemModule, false);
            foreach (var decl in sortedDecls) {
                if (decl is LiteralModuleDecl) {
                    // The declaration is a literal module, so it has members and such that we need
                    // to resolve. First we do refinement transformation. Then we construct the signature
                    // of the module. This is the public, externally visible signature. Then we add in 
                    // everything that the system defines, as well as any "import" (i.e. "opened" modules)
                    // directives (currently not supported, but this is where we would do it.) This signature,
                    // which is only used while resolving the members of the module is stored in the (basically)
                    // global variable moduleInfo. Then the signatures of the module members are resolved, followed
                    // by the bodies.
                    var literalDecl = (LiteralModuleDecl)decl;
                    var m = (literalDecl).ModuleDef;

                    var errorCount = ErrorCount;
                    rewriter.PreResolve(m);
                    ModuleSignature refinedSig = null;
                    if (m.RefinementBaseRoot != null) {
                        if (ResolvePath(m.RefinementBaseRoot, m.RefinementBaseName, out refinedSig)) {
                            if (refinedSig.ModuleDef != null) {
                                m.RefinementBase = refinedSig.ModuleDef;
                                refinementTransformer.PreResolve(m);
                            } else {
                                Error(m.RefinementBaseName[0], "module ({0}) named as refinement base is not a literal module or simple reference to a literal module", Util.Comma(".", m.RefinementBaseName, x => x.val));
                            }
                        } else {
                            Error(m.RefinementBaseName[0], "module ({0}) named as refinement base does not exist", Util.Comma(".", m.RefinementBaseName, x => x.val));
                        }
                    }
                    literalDecl.Signature = RegisterTopLevelDecls(m, true);
                    literalDecl.Signature.Refines = refinedSig;
                    var sig = literalDecl.Signature;
                    // set up environment
                    var preResolveErrorCount = ErrorCount;
                    useCompileSignatures = false;
                    ResolveModuleDefinition(m, sig);
                    if (ErrorCount == preResolveErrorCount) {
                        refinementTransformer.PostResolve(m);
                        // give rewriter a chance to do processing
                        rewriter.PostResolve(m);
                    }
                    if (ErrorCount == errorCount && !m.IsGhost) {
                        // compilation should only proceed if everything is good, including the signature (which preResolveErrorCount does not include);
                        var nw = (new Cloner()).CloneModuleDefinition(m, m.CompileName + "_Compile");
                        var compileSig = RegisterTopLevelDecls(nw, true);
                        compileSig.Refines = refinedSig;
                        sig.CompileSignature = compileSig;
                        useCompileSignatures = true;
                        ResolveModuleDefinition(nw, compileSig);
                        prog.CompileModules.Add(nw);
                    }
                } else if (decl is AliasModuleDecl) {
                    var alias = (AliasModuleDecl)decl;
                    // resolve the path
                    ModuleSignature p;
                    if (ResolvePath(alias.Root, alias.Path, out p)) {
                        alias.Signature = p;
                    } else {
                        alias.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature
                    }
                } else if (decl is AbstractModuleDecl) {
                    var abs = (AbstractModuleDecl)decl;
                    ModuleSignature p;
                    if (ResolvePath(abs.Root, abs.Path, out p)) {
                        abs.Signature = MakeAbstractSignature(p, abs.FullCompileName, abs.Height, prog.Modules);
                        abs.OriginalSignature = p;
                        ModuleSignature compileSig;
                        if (abs.CompilePath != null) {
                            if (ResolvePath(abs.CompileRoot, abs.CompilePath, out compileSig)) {
                                if (refinementTransformer.CheckIsRefinement(compileSig, p)) {
                                    abs.Signature.CompileSignature = compileSig;
                                } else {
                                    Error(abs.CompilePath[0],
                                    "module " + Util.Comma(".", abs.CompilePath, x => x.val) + " must be a refinement of " + Util.Comma(".", abs.Path, x => x.val));
                                }
                                abs.Signature.IsGhost = compileSig.IsGhost;
                                // always keep the ghost information, to supress a spurious error message when the compile module isn't actually a refinement
                            }
                        }
                    } else {
                        abs.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature
                    }
                } else { Contract.Assert(false); }
                Contract.Assert(decl.Signature != null);
            }
            // compute IsRecursive bit for mutually recursive functions
            foreach (ModuleDefinition m in prog.Modules) {
                foreach (var fn in ModuleDefinition.AllFunctions(m.TopLevelDecls)) {
                    if (!fn.IsRecursive) {  // note, self-recursion has already been determined
                        int n = m.CallGraph.GetSCCSize(fn);
                        if (2 <= n) {
                            // the function is mutually recursive (note, the SCC does not determine self recursion)
                            fn.IsRecursive = true;
                        }
                    }
                }
            }
        }

        private void ResolveModuleDefinition(ModuleDefinition m, ModuleSignature sig) {
            moduleInfo = MergeSignature(sig, systemNameInfo);
            // resolve
            var datatypeDependencies = new Graph<IndDatatypeDecl>();
            int prevErrorCount = ErrorCount;
            ResolveTopLevelDecls_Signatures(m, m.TopLevelDecls, datatypeDependencies);
            if (ErrorCount == prevErrorCount) {
                ResolveTopLevelDecls_Meat(m.TopLevelDecls, datatypeDependencies);
            }
        }


        public class ModuleBindings {
            private ModuleBindings parent;
            private Dictionary<string, ModuleDecl> modules;
            private Dictionary<string, ModuleBindings> bindings;

            public ModuleBindings(ModuleBindings p) {
                parent = p;
                modules = new Dictionary<string, ModuleDecl>();
                bindings = new Dictionary<string, ModuleBindings>();
            }
            public bool BindName(string name, ModuleDecl subModule, ModuleBindings b) {
                if (modules.ContainsKey(name)) {
                    return false;
                } else {
                    modules.Add(name, subModule);
                    bindings.Add(name, b);
                    return true;
                }
            }
            public bool TryLookup(IToken name, out ModuleDecl m) {
                Contract.Requires(name != null);
                if (modules.TryGetValue(name.val, out m)) {
                    return true;
                } else if (parent != null) {
                    return parent.TryLookup(name, out m);
                } else return false;
            }
            public bool TryLookupIgnore(IToken name, out ModuleDecl m, ModuleDecl ignore) {
                Contract.Requires(name != null);
                if (modules.TryGetValue(name.val, out m) && m != ignore) {
                    return true;
                } else if (parent != null) {
                    return parent.TryLookup(name, out m);
                } else return false;
            }
            public IEnumerable<ModuleDecl> ModuleList {
                get { return modules.Values; }
            }
            public ModuleBindings SubBindings(string name) {
                ModuleBindings v = null;
                bindings.TryGetValue(name, out v);
                return v;
            }
        }
        private ModuleBindings BindModuleNames(ModuleDefinition moduleDecl, ModuleBindings parentBindings) {
            var bindings = new ModuleBindings(parentBindings);

            foreach (var tld in moduleDecl.TopLevelDecls) {
                if (tld is LiteralModuleDecl) {
                    var subdecl = (LiteralModuleDecl)tld;
                    var subBindings = BindModuleNames(subdecl.ModuleDef, bindings);
                    if (!bindings.BindName(subdecl.Name, subdecl, subBindings)) {
                        Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
                    }
                } else if (tld is AbstractModuleDecl) {
                    var subdecl = (AbstractModuleDecl)tld;
                    if (!bindings.BindName(subdecl.Name, subdecl, null)) {
                        Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
                    }
                } else if (tld is AliasModuleDecl) {
                    var subdecl = (AliasModuleDecl)tld;
                    if (!bindings.BindName(subdecl.Name, subdecl, null)) {
                        Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name);
                    }
                }
            }
            return bindings;
        }

        private void ProcessDependenciesDefinition(ModuleDecl decl, ModuleDefinition m, ModuleBindings bindings, Graph<ModuleDecl> dependencies) {
            if (m.RefinementBaseName != null) {
                ModuleDecl other;
                if (!bindings.TryLookup(m.RefinementBaseName[0], out other)) {
                    Error(m, "module {0} named as refinement base does not exist", m.RefinementBaseName[0].val);
                } else if (other is LiteralModuleDecl && ((LiteralModuleDecl)other).ModuleDef == m) {
                    Error(m, "module cannot refine itself: {0}", m.RefinementBaseName[0].val);
                } else {
                    Contract.Assert(other != null);  // follows from postcondition of TryGetValue
                    dependencies.AddEdge(decl, other);
                    m.RefinementBaseRoot = other;
                }
            }
            foreach (var toplevel in m.TopLevelDecls) {
                if (toplevel is ModuleDecl) {
                    var d = (ModuleDecl)toplevel;
                    dependencies.AddEdge(decl, d);
                    var subbindings = bindings.SubBindings(d.Name);
                    ProcessDependencies(d, subbindings ?? bindings, dependencies);
                }
            }
        }
        private void ProcessDependencies(ModuleDecl moduleDecl, ModuleBindings bindings, Graph<ModuleDecl> dependencies) {
            dependencies.AddVertex(moduleDecl);
            if (moduleDecl is LiteralModuleDecl) {
                ProcessDependenciesDefinition(moduleDecl, ((LiteralModuleDecl)moduleDecl).ModuleDef, bindings, dependencies);
            } else if (moduleDecl is AliasModuleDecl) {
                var alias = moduleDecl as AliasModuleDecl;
                ModuleDecl root;
                if (!bindings.TryLookupIgnore(alias.Path[0], out root, alias))
                    Error(alias.tok, ModuleNotFoundErrorMessage(0, alias.Path));
                else {
                    dependencies.AddEdge(moduleDecl, root);
                    alias.Root = root;
                }
            } else if (moduleDecl is AbstractModuleDecl) {
                var abs = moduleDecl as AbstractModuleDecl;
                ModuleDecl root;
                if (!bindings.TryLookup(abs.Path[0], out root))
                    Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.Path));
                else {
                    dependencies.AddEdge(moduleDecl, root);
                    abs.Root = root;
                }
                if (abs.CompilePath != null) {
                    if (!bindings.TryLookup(abs.CompilePath[0], out root))
                        Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.CompilePath));
                    else {
                        dependencies.AddEdge(moduleDecl, root);
                        abs.CompileRoot = root;
                    }
                }
            }
        }

        private string ModuleNotFoundErrorMessage(int i, List<IToken> path) {
            return "module " + path[i].val + " does not exist" +
              (1 < path.Count ? " (position " + i.ToString() + " in path " + Util.Comma(".", path, x => x.val) + ")" : "");
        }

        public static ModuleSignature MergeSignature(ModuleSignature m, ModuleSignature system) {
            var info = new ModuleSignature();
            // add the system-declared information, among which we know there are no duplicates
            foreach (var kv in system.TopLevels) {
                info.TopLevels.Add(kv.Key, kv.Value);
            }
            foreach (var kv in system.Ctors) {
                info.Ctors.Add(kv.Key, kv.Value);
            }
            // add for the module itself
            foreach (var kv in m.TopLevels) {
                info.TopLevels[kv.Key] = kv.Value;
            }
            foreach (var kv in m.Ctors) {
                info.Ctors[kv.Key] = kv.Value;
            }
            foreach (var kv in m.StaticMembers) {
                info.StaticMembers[kv.Key] = kv.Value;
            }
            info.IsGhost = m.IsGhost;
            return info;
        }
        ModuleSignature RegisterTopLevelDecls(ModuleDefinition moduleDef, bool useImports) {
            Contract.Requires(moduleDef != null);
            var sig = new ModuleSignature();
            sig.ModuleDef = moduleDef;
            sig.IsGhost = moduleDef.IsGhost;
            List<TopLevelDecl> declarations = moduleDef.TopLevelDecls;

            if (useImports) {
                // First go through and add anything from the opened imports
                foreach (var im in declarations) {
                    if (im is ModuleDecl && ((ModuleDecl)im).Opened) {
                        var s = ((ModuleDecl)im).Signature;
                        // classes:
                        foreach (var kv in s.TopLevels) {
                            TopLevelDecl d;
                            if (sig.TopLevels.TryGetValue(kv.Key, out d)) {
                                sig.TopLevels[kv.Key] = new AmbiguousTopLevelDecl(moduleDef, d, kv.Value);
                            } else {
                                sig.TopLevels.Add(kv.Key, kv.Value);
                            }
                        }
                        // constructors:
                        foreach (var kv in s.Ctors) {
                            Tuple<DatatypeCtor, bool> pair;
                            if (sig.Ctors.TryGetValue(kv.Key, out pair)) {
                                // mark it as a duplicate
                                sig.Ctors[kv.Key] = new Tuple<DatatypeCtor, bool>(pair.Item1, true);
                            } else {
                                // add new
                                sig.Ctors.Add(kv.Key, kv.Value);
                            }
                        }
                        // static members:
                        foreach (var kv in s.StaticMembers) {
                            MemberDecl md;
                            if (sig.StaticMembers.TryGetValue(kv.Key, out md)) {
                                sig.StaticMembers[kv.Key] = new AmbiguousMemberDecl(moduleDef, md, kv.Value);
                            } else {
                                // add new
                                sig.StaticMembers.Add(kv.Key, kv.Value);
                            }
                        }
                    }
                }
            }
            // This is solely used to detect duplicates amongst the various e
            Dictionary<string, TopLevelDecl> toplevels = new Dictionary<string, TopLevelDecl>();
            // Now add the things present
            foreach (TopLevelDecl d in declarations) {
                Contract.Assert(d != null);
                // register the class/datatype/module name
                if (toplevels.ContainsKey(d.Name)) {
                    Error(d, "Duplicate name of top-level declaration: {0}", d.Name);
                } else {
                    toplevels[d.Name] = d;
                    sig.TopLevels[d.Name] = d;
                }
                if (d is ModuleDecl) {
                    // nothing to do
                } else if (d is ArbitraryTypeDecl) {
                    // nothing more to register

                } else if (d is IteratorDecl) {
                    var iter = (IteratorDecl)d;

                    // register the names of the implicit members
                    var members = new Dictionary<string, MemberDecl>();
                    classMembers.Add(iter, members);

                    // First, register the iterator's in- and out-parameters as readonly fields
                    foreach (var p in iter.Ins) {
                        if (members.ContainsKey(p.Name)) {
                            Error(p, "Name of in-parameter is used by another member of the iterator: {0}", p.Name);
                        } else {
                            var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, false, false, p.Type, null);
                            field.EnclosingClass = iter;  // resolve here
                            members.Add(p.Name, field);
                            iter.Members.Add(field);
                        }
                    }
                    foreach (var p in iter.Outs) {
                        if (members.ContainsKey(p.Name)) {
                            Error(p, "Name of yield-parameter is used by another member of the iterator: {0}", p.Name);
                        } else {
                            var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, true, true, p.Type, null);
                            field.EnclosingClass = iter;  // resolve here
                            iter.OutsFields.Add(field);
                            members.Add(p.Name, field);
                            iter.Members.Add(field);
                        }
                    }
                    foreach (var p in iter.Outs) {
                        var nm = p.Name + "s";
                        if (members.ContainsKey(nm)) {
                            Error(p.tok, "Name of implicit yield-history variable '{0}' is already used by another member of the iterator", p.Name);
                        } else {
                            var tp = new SeqType(p.Type.IsSubrangeType ? new IntType() : p.Type);
                            var field = new SpecialField(p.tok, nm, nm, "", "", true, true, false, tp, null);
                            field.EnclosingClass = iter;  // resolve here
                            iter.OutsHistoryFields.Add(field);  // for now, just record this field (until all parameters have been added as members)
                        }
                    }
                    // now that already-used 'ys' names have been checked for, add these yield-history variables
                    iter.OutsHistoryFields.ForEach(f => {
                        members.Add(f.Name, f);
                        iter.Members.Add(f);
                    });
                    // add the additional special variables as fields
                    iter.Member_Reads = new SpecialField(iter.tok, "_reads", "_reads", "", "", true, false, false, new SetType(new ObjectType()), null);
                    iter.Member_Modifies = new SpecialField(iter.tok, "_modifies", "_modifies", "", "", true, false, false, new SetType(new ObjectType()), null);
                    iter.Member_New = new SpecialField(iter.tok, "_new", "_new", "", "", true, true, true, new SetType(new ObjectType()), null);
                    foreach (var field in new List<Field>() { iter.Member_Reads, iter.Member_Modifies, iter.Member_New }) {
                        field.EnclosingClass = iter;  // resolve here
                        members.Add(field.Name, field);
                        iter.Members.Add(field);
                    }
                    // finally, add special variables to hold the components of the (explicit or implicit) decreases clause
                    bool inferredDecreases;
                    var decr = Translator.MethodDecreasesWithDefault(iter, out inferredDecreases);
                    if (inferredDecreases) {
                        iter.InferredDecreases = true;
                        Contract.Assert(iter.Decreases.Expressions.Count == 0);
                        iter.Decreases.Expressions.AddRange(decr);
                    }
                    // create the fields; unfortunately, we don't know their types yet, so we'll just insert type proxies for now
                    var i = 0;
                    foreach (var p in iter.Decreases.Expressions) {
                        var nm = "_decreases" + i;
                        var field = new SpecialField(p.tok, nm, nm, "", "", true, false, false, new InferredTypeProxy(), null);
                        field.EnclosingClass = iter;  // resolve here
                        iter.DecreasesFields.Add(field);
                        members.Add(field.Name, field);
                        iter.Members.Add(field);
                        i++;
                    }

                    // Note, the typeArgs parameter to the following Method/Predicate constructors is passed in as the empty list.  What that is
                    // saying is that the Method/Predicate does not take any type parameters over and beyond what the enclosing type (namely, the
                    // iterator type) does.
                    // --- here comes the constructor
                    var init = new Constructor(iter.tok, iter.Name, new List<TypeParameter>(), iter.Ins,
                      new List<PatternExpression>() /* Yuyan */,
                      new List<MaybeFreeExpression>(),
                      new Specification<FrameExpression>(new List<FrameExpression>(), null),
                      new List<MaybeFreeExpression>(),
                      new Specification<Expression>(new List<Expression>(), null),
                      null, null, false);
                    // --- here comes predicate Valid()
                    var valid = new Predicate(iter.tok, "Valid", false, true, new List<TypeParameter>(), iter.tok,
                      new List<Formal>(),
                      new List<PatternExpression>() /* Yuyan */,
                      new List<Expression>(),
                      new List<FrameExpression>(),
                      new List<Expression>(),
                      new Specification<Expression>(new List<Expression>(), null),
                      null, Predicate.BodyOriginKind.OriginalOrInherited, null, false);
                    // --- here comes method MoveNext
                    var moveNext = new Method(iter.tok, "MoveNext", false, false, new List<TypeParameter>(),
                      new List<Formal>(), new List<Formal>() { new Formal(iter.tok, "more", Type.Bool, false, false) },
                      new List<PatternExpression>() /* Yuyan */,
                      new List<MaybeFreeExpression>(),
                      new Specification<FrameExpression>(new List<FrameExpression>(), null),
                      new List<MaybeFreeExpression>(),
                      new Specification<Expression>(new List<Expression>(), null),
                      null, null, false);
                    // add these implicit members to the class
                    init.EnclosingClass = iter;
                    valid.EnclosingClass = iter;
                    moveNext.EnclosingClass = iter;
                    iter.HasConstructor = true;
                    iter.Member_Init = init;
                    iter.Member_Valid = valid;
                    iter.Member_MoveNext = moveNext;
                    MemberDecl member;
                    if (members.TryGetValue(init.Name, out member)) {
                        Error(member.tok, "member name '{0}' is already predefined for this iterator", init.Name);
                    } else {
                        members.Add(init.Name, init);
                        iter.Members.Add(init);
                    }
                    // If the name of the iterator is "Valid" or "MoveNext", one of the following will produce an error message.  That
                    // error message may not be as clear as it could be, but the situation also seems unlikely to ever occur in practice.
                    if (members.TryGetValue("Valid", out member)) {
                        Error(member.tok, "member name 'Valid' is already predefined for iterators");
                    } else {
                        members.Add(valid.Name, valid);
                        iter.Members.Add(valid);
                    }
                    if (members.TryGetValue("MoveNext", out member)) {
                        Error(member.tok, "member name 'MoveNext' is already predefined for iterators");
                    } else {
                        members.Add(moveNext.Name, moveNext);
                        iter.Members.Add(moveNext);
                    }

                } else if (d is ClassDecl) {
                    ClassDecl cl = (ClassDecl)d;

                    // register the names of the class members
                    var members = new Dictionary<string, MemberDecl>();
                    classMembers.Add(cl, members);

                    bool hasConstructor = false;
                    foreach (MemberDecl m in cl.Members) {
                        if (members.ContainsKey(m.Name)) {
                            Error(m, "Duplicate member name: {0}", m.Name);
                        } else {
                            members.Add(m.Name, m);
                        }
                        if (m is Constructor) {
                            hasConstructor = true;
                        }
                    }
                    cl.HasConstructor = hasConstructor;
                    if (cl.IsDefaultClass) {
                        foreach (MemberDecl m in cl.Members) {
                            if (m.IsStatic && (m is Function || m is Method)) {
                                sig.StaticMembers[m.Name] = m;
                            }
                        }
                    }

                } else {
                    DatatypeDecl dt = (DatatypeDecl)d;

                    // register the names of the constructors
                    var ctors = new Dictionary<string, DatatypeCtor>();
                    datatypeCtors.Add(dt, ctors);
                    // ... and of the other members
                    var members = new Dictionary<string, MemberDecl>();
                    datatypeMembers.Add(dt, members);

                    foreach (DatatypeCtor ctor in dt.Ctors) {
                        if (ctor.Name.EndsWith("?")) {
                            Error(ctor, "a datatype constructor name is not allowed to end with '?'");
                        } else if (ctors.ContainsKey(ctor.Name)) {
                            Error(ctor, "Duplicate datatype constructor name: {0}", ctor.Name);
                        } else {
                            ctors.Add(ctor.Name, ctor);

                            // create and add the query "method" (field, really)
                            string queryName = ctor.Name + "?";
                            var query = new SpecialField(ctor.tok, queryName, "is_" + ctor.CompileName, "", "", false, false, false, Type.Bool, null);
                            query.EnclosingClass = dt;  // resolve here
                            members.Add(queryName, query);
                            ctor.QueryField = query;

                            // also register the constructor name globally
                            Tuple<DatatypeCtor, bool> pair;
                            if (sig.Ctors.TryGetValue(ctor.Name, out pair)) {
                                // mark it as a duplicate
                                sig.Ctors[ctor.Name] = new Tuple<DatatypeCtor, bool>(pair.Item1, true);
                            } else {
                                // add new
                                sig.Ctors.Add(ctor.Name, new Tuple<DatatypeCtor, bool>(ctor, false));
                            }
                        }
                    }
                    // add deconstructors now (that is, after the query methods have been added)
                    foreach (DatatypeCtor ctor in dt.Ctors) {
                        foreach (var formal in ctor.Formals) {
                            SpecialField dtor = null;
                            if (formal.HasName) {
                                if (members.ContainsKey(formal.Name)) {
                                    Error(ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name);
                                } else {
                                    dtor = new DatatypeDestructor(formal.tok, ctor, formal, formal.Name, "dtor_" + formal.Name, "", "", formal.IsGhost, formal.Type, null);
                                    dtor.EnclosingClass = dt;  // resolve here
                                    members.Add(formal.Name, dtor);
                                }
                            }
                            ctor.Destructors.Add(dtor);
                        }
                    }
                }
            }
            return sig;
        }

        private ModuleSignature MakeAbstractSignature(ModuleSignature p, string Name, int Height, List<ModuleDefinition> mods) {
            var mod = new ModuleDefinition(Token.NoToken, Name + ".Abs", true, true, null, null, false);
            mod.Height = Height;
            foreach (var kv in p.TopLevels) {
                mod.TopLevelDecls.Add(CloneDeclaration(kv.Value, mod, mods, Name));
            }
            var sig = RegisterTopLevelDecls(mod, false);
            sig.Refines = p.Refines;
            sig.CompileSignature = p;
            sig.IsGhost = p.IsGhost;
            mods.Add(mod);
            ResolveModuleDefinition(mod, sig);
            return sig;
        }
        TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition m, List<ModuleDefinition> mods, string Name) {
            Contract.Requires(d != null);
            Contract.Requires(m != null);

            if (d is ArbitraryTypeDecl) {
                var dd = (ArbitraryTypeDecl)d;
                return new ArbitraryTypeDecl(dd.tok, dd.Name, m, dd.EqualitySupport, null);
            } else if (d is IndDatatypeDecl) {
                var dd = (IndDatatypeDecl)d;
                var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
                var ctors = dd.Ctors.ConvertAll(CloneCtor);
                var dt = new IndDatatypeDecl(dd.tok, dd.Name, m, tps, ctors, null);
                return dt;
            } else if (d is CoDatatypeDecl) {
                var dd = (CoDatatypeDecl)d;
                var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
                var ctors = dd.Ctors.ConvertAll(CloneCtor);
                var dt = new CoDatatypeDecl(dd.tok, dd.Name, m, tps, ctors, null);
                return dt;
            } else if (d is ClassDecl) {
                var dd = (ClassDecl)d;
                var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
                var mm = dd.Members.ConvertAll(CloneMember);
                if (dd is DefaultClassDecl) {
                    return new DefaultClassDecl(m, mm);
                } else return new ClassDecl(dd.tok, dd.Name, m, tps, mm, null);
            } else if (d is ModuleDecl) {
                if (d is LiteralModuleDecl) {
                    return new LiteralModuleDecl(((LiteralModuleDecl)d).ModuleDef, m);
                } else if (d is AliasModuleDecl) {
                    var a = (AliasModuleDecl)d;
                    var alias = new AliasModuleDecl(a.Path, a.tok, m, a.Opened);
                    alias.ModuleReference = a.ModuleReference;
                    alias.Signature = a.Signature;
                    return alias;
                } else if (d is AbstractModuleDecl) {
                    var abs = (AbstractModuleDecl)d;
                    var sig = MakeAbstractSignature(abs.OriginalSignature, Name + "." + abs.Name, abs.Height, mods);
                    var a = new AbstractModuleDecl(abs.Path, abs.tok, m, abs.CompilePath, abs.Opened);
                    a.Signature = sig;
                    a.OriginalSignature = abs.OriginalSignature;
                    return a;
                } else {
                    Contract.Assert(false);  // unexpected declaration
                    return null;  // to please compiler
                }
            } else {
                Contract.Assert(false);  // unexpected declaration
                return null;  // to please compiler
            }
        }
        MemberDecl CloneMember(MemberDecl member) {
            if (member is Field) {
                Contract.Assert(!(member is SpecialField));  // we don't expect a SpecialField to be cloned (or do we?)
                var f = (Field)member;
                return new Field(f.tok, f.Name, f.IsGhost, f.IsMutable, f.IsUserMutable, CloneType(f.Type), null);
            } else if (member is Function) {
                var f = (Function)member;
                return CloneFunction(f.tok, f, f.IsGhost);
            } else {
                var m = (Method)member;
                return CloneMethod(m);
            }
        }
        TypeParameter CloneTypeParam(TypeParameter tp) {
            return new TypeParameter(tp.tok, tp.Name);
        }

        DatatypeCtor CloneCtor(DatatypeCtor ct) {
            return new DatatypeCtor(ct.tok, ct.Name, ct.Formals.ConvertAll(CloneFormal), null);
        }
        Formal CloneFormal(Formal formal) {
            return new Formal(formal.tok, formal.Name, CloneType(formal.Type), formal.InParam, formal.IsGhost);
        }

        /* Yuyan */
        PatternExpression ClonePattern(PatternExpression pattern) {
            return new PatternExpression(pattern.tok, CloneExpr(pattern.E));
        }

        Type CloneType(Type t) {
            if (t is BasicType) {
                return t;
            } else if (t is SetType) {
                var tt = (SetType)t;
                return new SetType(CloneType(tt.Arg));
            } else if (t is SeqType) {
                var tt = (SeqType)t;
                return new SeqType(CloneType(tt.Arg));
            } else if (t is MultiSetType) {
                var tt = (MultiSetType)t;
                return new MultiSetType(CloneType(tt.Arg));
            } else if (t is MapType) {
                var tt = (MapType)t;
                return new MapType(CloneType(tt.Domain), CloneType(tt.Range));
            } else if (t is UserDefinedType) {
                var tt = (UserDefinedType)t;
                return new UserDefinedType(tt.tok, tt.Name, tt.TypeArgs.ConvertAll(CloneType), tt.Path.ConvertAll(x => x));
            } else if (t is InferredTypeProxy) {
                return new InferredTypeProxy();
            } else {
                Contract.Assert(false);  // unexpected type (e.g., no other type proxies are expected at this time)
                return null;  // to please compiler
            }
        }
        Function CloneFunction(IToken tok, Function f, bool isGhost) {

            var tps = f.TypeArgs.ConvertAll(CloneTypeParam);
            var formals = f.Formals.ConvertAll(CloneFormal);
            var patterns = f.Patterns.ConvertAll(ClonePattern); // Yuyan
            var req = f.Req.ConvertAll(CloneExpr);
            var reads = f.Reads.ConvertAll(CloneFrameExpr);
            var decreases = CloneSpecExpr(f.Decreases);

            var ens = f.Ens.ConvertAll(CloneExpr);

            Expression body = CloneExpr(f.Body);

            if (f is Predicate) {
                return new Predicate(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals, patterns /* Yuyan */,
                  req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, null, false);
            } else if (f is CoPredicate) {
                return new CoPredicate(tok, f.Name, f.IsStatic, tps, f.OpenParen, formals, patterns /* Yuyan */,
                  req, reads, ens, body, null, false);
            } else {
                return new Function(tok, f.Name, f.IsStatic, isGhost, tps, f.OpenParen, formals, patterns /* Yuyan */, CloneType(f.ResultType),
                req, reads, ens, decreases, body, null, false);
            }
        }
        Method CloneMethod(Method m) {
            Contract.Requires(m != null);

            var tps = m.TypeArgs.ConvertAll(CloneTypeParam);
            var ins = m.Ins.ConvertAll(CloneFormal);
            var patterns = m.Patterns.ConvertAll(ClonePattern);
            var req = m.Req.ConvertAll(CloneMayBeFreeExpr);
            var mod = CloneSpecFrameExpr(m.Mod);
            var decreases = CloneSpecExpr(m.Decreases);

            var ens = m.Ens.ConvertAll(CloneMayBeFreeExpr);

            if (m is Constructor) {
                return new Constructor(m.tok, m.Name, tps, ins, patterns /* Yuyan */,
                  req, mod, ens, decreases, null, null, false);
            } else {
                return new Method(m.tok, m.Name, m.IsStatic, m.IsGhost, tps, ins, m.Outs.ConvertAll(CloneFormal), patterns /* Yuyan */,
                  req, mod, ens, decreases, null, null, false);
            }
        }
        Specification<Expression> CloneSpecExpr(Specification<Expression> spec) {
            var ee = spec.Expressions == null ? null : spec.Expressions.ConvertAll(CloneExpr);
            return new Specification<Expression>(ee, null);
        }
        Specification<FrameExpression> CloneSpecFrameExpr(Specification<FrameExpression> frame) {
            var ee = frame.Expressions == null ? null : frame.Expressions.ConvertAll(CloneFrameExpr);
            return new Specification<FrameExpression>(ee, null);
        }
        FrameExpression CloneFrameExpr(FrameExpression frame) {
            return new FrameExpression(frame.tok, CloneExpr(frame.E), frame.FieldName);
        }
        MaybeFreeExpression CloneMayBeFreeExpr(MaybeFreeExpression expr) {
            return new MaybeFreeExpression(CloneExpr(expr.E), expr.IsFree);
        }
        BoundVar CloneBoundVar(BoundVar bv) {
            return new BoundVar(bv.tok, bv.Name, CloneType(bv.Type));
        }
        Expression CloneExpr(Expression expr) {
            if (expr == null) {
                return null;
            } else if (expr is LiteralExpr) {
                var e = (LiteralExpr)expr;
                if (e.Value == null) {
                    return new LiteralExpr(e.tok);
                } else if (e.Value is bool) {
                    return new LiteralExpr(e.tok, (bool)e.Value);
                } else {
                    return new LiteralExpr(e.tok, (BigInteger)e.Value);
                }

            } else if (expr is ThisExpr) {
                if (expr is ImplicitThisExpr) {
                    return new ImplicitThisExpr(expr.tok);
                } else {
                    return new ThisExpr(expr.tok);
                }

            } else if (expr is IdentifierExpr) {
                var e = (IdentifierExpr)expr;
                return new IdentifierExpr(e.tok, e.Name);

            } else if (expr is DatatypeValue) {
                var e = (DatatypeValue)expr;
                return new DatatypeValue(e.tok, e.DatatypeName, e.MemberName, e.Arguments.ConvertAll(CloneExpr));

            } else if (expr is DisplayExpression) {
                DisplayExpression e = (DisplayExpression)expr;
                if (expr is SetDisplayExpr) {
                    return new SetDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
                } else if (expr is MultiSetDisplayExpr) {
                    return new MultiSetDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
                } else {
                    Contract.Assert(expr is SeqDisplayExpr);
                    return new SeqDisplayExpr(e.tok, e.Elements.ConvertAll(CloneExpr));
                }

            } else if (expr is MapDisplayExpr) {
                MapDisplayExpr e = (MapDisplayExpr)expr;
                List<ExpressionPair> pp = new List<ExpressionPair>();
                foreach (ExpressionPair p in e.Elements) {
                    pp.Add(new ExpressionPair(CloneExpr(p.A), CloneExpr(p.B)));
                }
                return new MapDisplayExpr(expr.tok, pp);
            } else if (expr is ExprDotName) {
                var e = (ExprDotName)expr;
                return new ExprDotName(e.tok, CloneExpr(e.Obj), e.SuffixName);

            } else if (expr is FieldSelectExpr) {
                var e = (FieldSelectExpr)expr;
                return new FieldSelectExpr(e.tok, CloneExpr(e.Obj), e.FieldName);

            } else if (expr is SeqSelectExpr) {
                var e = (SeqSelectExpr)expr;
                return new SeqSelectExpr(e.tok, e.SelectOne, CloneExpr(e.Seq), CloneExpr(e.E0), CloneExpr(e.E1));

            } else if (expr is MultiSelectExpr) {
                var e = (MultiSelectExpr)expr;
                return new MultiSelectExpr(e.tok, CloneExpr(e.Array), e.Indices.ConvertAll(CloneExpr));

            } else if (expr is SeqUpdateExpr) {
                var e = (SeqUpdateExpr)expr;
                return new SeqUpdateExpr(e.tok, CloneExpr(e.Seq), CloneExpr(e.Index), CloneExpr(e.Value));

            } else if (expr is FunctionCallExpr) {
                var e = (FunctionCallExpr)expr;
                return new FunctionCallExpr(e.tok, e.Name, CloneExpr(e.Receiver), e.OpenParen == null ? null : (e.OpenParen), e.Args.ConvertAll(CloneExpr));

            } else if (expr is OldExpr) {
                var e = (OldExpr)expr;
                return new OldExpr(e.tok, CloneExpr(e.E));

            } else if (expr is MultiSetFormingExpr) {
                var e = (MultiSetFormingExpr)expr;
                return new MultiSetFormingExpr(e.tok, CloneExpr(e.E));

            } else if (expr is FreshExpr) {
                var e = (FreshExpr)expr;
                return new FreshExpr(e.tok, CloneExpr(e.E));

            } else if (expr is UnaryExpr) {
                var e = (UnaryExpr)expr;
                return new UnaryExpr(e.tok, e.Op, CloneExpr(e.E));

            } else if (expr is BinaryExpr) {
                var e = (BinaryExpr)expr;
                return new BinaryExpr(e.tok, e.Op, CloneExpr(e.E0), CloneExpr(e.E1));

            } else if (expr is ChainingExpression) {
                var e = (ChainingExpression)expr;
                return CloneExpr(e.E);  // just clone the desugaring, since it's already available

            } else if (expr is LetExpr) {
                var e = (LetExpr)expr;
                return new LetExpr(e.tok, e.Vars.ConvertAll(CloneBoundVar), e.RHSs.ConvertAll(CloneExpr), CloneExpr(e.Body));

            } else if (expr is ComprehensionExpr) {
                var e = (ComprehensionExpr)expr;
                var tk = e.tok;
                var bvs = e.BoundVars.ConvertAll(CloneBoundVar);
                var range = CloneExpr(e.Range);
                var term = CloneExpr(e.Term);
                if (e is ForallExpr) {
                    return new ForallExpr(tk, bvs, range, term, null);
                } else if (e is ExistsExpr) {
                    return new ExistsExpr(tk, bvs, range, term, null);
                } else if (e is MapComprehension) {
                    return new MapComprehension(tk, bvs, range, term);
                } else {
                    Contract.Assert(e is SetComprehension);
                    return new SetComprehension(tk, bvs, range, term);
                }

            } else if (expr is WildcardExpr) {
                return new WildcardExpr(expr.tok);

            } else if (expr is PredicateExpr) {
                var e = (PredicateExpr)expr;
                if (e is AssertExpr) {
                    return new AssertExpr(e.tok, CloneExpr(e.Guard), CloneExpr(e.Body));
                } else {
                    Contract.Assert(e is AssumeExpr);
                    return new AssumeExpr(e.tok, CloneExpr(e.Guard), CloneExpr(e.Body));
                }

            } else if (expr is ITEExpr) {
                var e = (ITEExpr)expr;
                return new ITEExpr(e.tok, CloneExpr(e.Test), CloneExpr(e.Thn), CloneExpr(e.Els));

            } else if (expr is ParensExpression) {
                var e = (ParensExpression)expr;
                return CloneExpr(e.E);  // skip the parentheses in the clone

            } else if (expr is IdentifierSequence) {
                var e = (IdentifierSequence)expr;
                var aa = e.Arguments == null ? null : e.Arguments.ConvertAll(CloneExpr);
                return new IdentifierSequence(e.Tokens.ConvertAll(tk => (tk)), e.OpenParen == null ? null : (e.OpenParen), aa);

            } else if (expr is MatchExpr) {
                var e = (MatchExpr)expr;
                return new MatchExpr(e.tok, CloneExpr(e.Source),
                  e.Cases.ConvertAll(c => new MatchCaseExpr(c.tok, c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body))));

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected expression
            }
        }

        private bool ResolvePath(ModuleDecl root, List<IToken> Path, out ModuleSignature p) {
            p = root.Signature;
            int i = 1;
            while (i < Path.Count) {
                ModuleSignature pp;
                if (p.FindSubmodule(Path[i].val, out pp)) {
                    p = pp;
                    i++;
                } else {
                    Error(Path[i], ModuleNotFoundErrorMessage(i, Path));
                    break;
                }
            }
            return i == Path.Count;
        }
        public void ResolveTopLevelDecls_Signatures(ModuleDefinition def, List<TopLevelDecl/*!*/>/*!*/ declarations, Graph<IndDatatypeDecl/*!*/>/*!*/ datatypeDependencies) {
            Contract.Requires(declarations != null);
            Contract.Requires(datatypeDependencies != null);  // more expensive check: Contract.Requires(cce.NonNullElements(datatypeDependencies));
            foreach (TopLevelDecl d in declarations) {
                Contract.Assert(d != null);
                allTypeParameters.PushMarker();
                ResolveTypeParameters(d.TypeArgs, true, d);
                if (d is ArbitraryTypeDecl) {
                    // nothing to do
                } else if (d is IteratorDecl) {
                    ResolveIteratorSignature((IteratorDecl)d);
                } else if (d is ClassDecl) {
                    ResolveClassMemberTypes((ClassDecl)d);
                } else if (d is ModuleDecl) {
                    var decl = (ModuleDecl)d;
                    if (!def.IsGhost) {
                        if (decl.Signature.IsGhost) {
                            if (!(def.IsDefaultModule)) // _module is allowed to contain ghost modules, but not be ghost itself. Note this presents a challenge to
                                // trusted verification, as toplevels can't be trusted if they invoke ghost module members.
                                Error(d.tok, "ghost modules can only be imported into other ghost modules, not physical ones.");
                        } else {
                            // physical modules are allowed everywhere
                        }
                    } else {
                        // everything is allowed in a ghost module
                    }
                } else {
                    ResolveCtorTypes((DatatypeDecl)d, datatypeDependencies);
                }
                allTypeParameters.PopMarker();
            }
        }

        public void ResolveTopLevelDecls_Meat(List<TopLevelDecl/*!*/>/*!*/ declarations, Graph<IndDatatypeDecl/*!*/>/*!*/ datatypeDependencies) {
            Contract.Requires(declarations != null);
            Contract.Requires(cce.NonNullElements(datatypeDependencies));

            int prevErrorCount = ErrorCount;

            // Resolve the meat of classes, and the type parameters of all top-level type declarations
            foreach (TopLevelDecl d in declarations) {
                Contract.Assert(d != null);
                allTypeParameters.PushMarker();
                ResolveTypeParameters(d.TypeArgs, false, d);
                if (d is IteratorDecl) {
                    var iter = (IteratorDecl)d;
                    allTypeParameters.PushMarker();
                    ResolveTypeParameters(iter.TypeArgs, false, iter);
                    ResolveIterator(iter);
                    allTypeParameters.PopMarker();
                    ResolveClassMemberBodies(iter);  // resolve the automatically generated members

                } else if (d is ClassDecl) {
                    var cl = (ClassDecl)d;
                    ResolveAttributes(cl.Attributes, false);
                    ResolveClassMemberBodies(cl);
                }
                allTypeParameters.PopMarker();
            }

            if (ErrorCount == prevErrorCount) {
                foreach (TopLevelDecl d in declarations) {
                    if (d is ClassDecl) {
                        foreach (var member in ((ClassDecl)d).Members) {
                            if (member is Method) {
                                var m = (Method)member;
                                if (m.Body != null) {
                                    CheckTypeInference(m.Body);
                                    bool tail = true;
                                    bool hasTailRecursionPreference = Attributes.ContainsBool(m.Attributes, "tailrecursion", ref tail);
                                    if (hasTailRecursionPreference && !tail) {
                                        // the user specifically requested no tail recursion, so do nothing else
                                    } else if (hasTailRecursionPreference && tail && m.IsGhost) {
                                        Error(m.tok, "tail recursion can be specified only for methods that will be compiled, not for ghost methods");
                                    } else {
                                        var module = m.EnclosingClass.Module;
                                        var sccSize = module.CallGraph.GetSCCSize(m);
                                        if (hasTailRecursionPreference && 2 <= sccSize) {
                                            Error(m.tok, "sorry, tail-call optimizations are not supported for mutually recursive methods");
                                        } else if (hasTailRecursionPreference || sccSize == 1) {
                                            CallStmt tailCall = null;
                                            var status = CheckTailRecursive(m.Body.Body, m, ref tailCall, hasTailRecursionPreference);
                                            if (status != TailRecursionStatus.NotTailRecursive) {
                                                m.IsTailRecursive = true;
                                            }
                                        }
                                    }
                                }
                                if (!m.IsTailRecursive && m.Body != null && Contract.Exists(m.Decreases.Expressions, e => e is WildcardExpr)) {
                                    Error(m.Decreases.Expressions[0].tok, "'decreases *' is allowed only on tail-recursive methods");
                                }
                            } else if (member is Function) {
                                var f = (Function)member;
                                if (f.Body != null) {
                                    CheckTypeInference(f.Body);
                                    bool tail = true;
                                    if (Attributes.ContainsBool(f.Attributes, "tailrecursion", ref tail) && tail) {
                                        Error(f.tok, "sorry, tail-call functions are not supported");
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // Perform the stratosphere check on inductive datatypes, and compute to what extent the inductive datatypes require equality support
            foreach (var dtd in datatypeDependencies.TopologicallySortedComponents()) {
                if (datatypeDependencies.GetSCCRepresentative(dtd) == dtd) {
                    // do the following check once per SCC, so call it on each SCC representative
                    SccStratosphereCheck(dtd, datatypeDependencies);
                    DetermineEqualitySupport(dtd, datatypeDependencies);
                }
            }

            if (ErrorCount == prevErrorCount) {  // because CheckCoCalls requires the given expression to have been successfully resolved
                // Perform the guardedness check on co-datatypes
                foreach (var fn in ModuleDefinition.AllFunctions(declarations)) {
                    var module = fn.EnclosingClass.Module;
                    if (fn.Body != null && module.CallGraph.GetSCCRepresentative(fn) == fn) {
                        bool dealsWithCodatatypes = false;
                        foreach (var m in module.CallGraph.GetSCC(fn)) {
                            var f = (Function)m;
                            if (f.ResultType.InvolvesCoDatatype) {
                                dealsWithCodatatypes = true;
                                break;
                            }
                        }
                        foreach (var m in module.CallGraph.GetSCC(fn)) {
                            var f = (Function)m;
                            var checker = new CoCallResolution(f, dealsWithCodatatypes);
                            checker.CheckCoCalls(f.Body);
                        }
                    }
                }
                // Inferred required equality support for datatypes and for Function and Method signatures
                // First, do datatypes until a fixpoint is reached
                bool inferredSomething;
                do {
                    inferredSomething = false;
                    foreach (var d in declarations) {
                        if (d is DatatypeDecl) {
                            var dt = (DatatypeDecl)d;
                            foreach (var tp in dt.TypeArgs) {
                                if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
                                    // here's our chance to infer the need for equality support
                                    foreach (var ctor in dt.Ctors) {
                                        foreach (var arg in ctor.Formals) {
                                            if (InferRequiredEqualitySupport(tp, arg.Type)) {
                                                tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
                                                inferredSomething = true;
                                                goto DONE_DT;  // break out of the doubly-nested loop
                                            }
                                        }
                                    }
                                DONE_DT: ;
                                }
                            }
                        }
                    }
                } while (inferredSomething);
                // Now do it for Function and Method signatures
                foreach (var d in declarations) {
                    if (d is IteratorDecl) {
                        var iter = (IteratorDecl)d;
                        foreach (var tp in iter.TypeArgs) {
                            if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
                                // here's our chance to infer the need for equality support
                                foreach (var p in iter.Ins) {
                                    if (InferRequiredEqualitySupport(tp, p.Type)) {
                                        tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
                                        goto DONE;
                                    }
                                }
                                foreach (var p in iter.Outs) {
                                    if (InferRequiredEqualitySupport(tp, p.Type)) {
                                        tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
                                        goto DONE;
                                    }
                                }
                            DONE: ;
                            }
                        }
                    } else if (d is ClassDecl) {
                        var cl = (ClassDecl)d;
                        foreach (var member in cl.Members) {
                            if (!member.IsGhost) {
                                if (member is Function) {
                                    var f = (Function)member;
                                    foreach (var tp in f.TypeArgs) {
                                        if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
                                            // here's our chance to infer the need for equality support
                                            if (InferRequiredEqualitySupport(tp, f.ResultType)) {
                                                tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
                                            } else {
                                                foreach (var p in f.Formals) {
                                                    if (InferRequiredEqualitySupport(tp, p.Type)) {
                                                        tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else if (member is Method) {
                                    var m = (Method)member;
                                    foreach (var tp in m.TypeArgs) {
                                        if (tp.EqualitySupport == TypeParameter.EqualitySupportValue.Unspecified) {
                                            // here's our chance to infer the need for equality support
                                            foreach (var p in m.Ins) {
                                                if (InferRequiredEqualitySupport(tp, p.Type)) {
                                                    tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
                                                    goto DONE;
                                                }
                                            }
                                            foreach (var p in m.Outs) {
                                                if (InferRequiredEqualitySupport(tp, p.Type)) {
                                                    tp.EqualitySupport = TypeParameter.EqualitySupportValue.InferredRequired;
                                                    goto DONE;
                                                }
                                            }
                                        DONE: ;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                // Check that all == and != operators in non-ghost contexts are applied to equality-supporting types.
                // Note that this check can only be done after determining which expressions are ghosts.
                foreach (var d in declarations) {
                    if (d is IteratorDecl) {
                        var iter = (IteratorDecl)d;
                        foreach (var p in iter.Ins) {
                            if (!p.IsGhost) {
                                CheckEqualityTypes_Type(p.tok, p.Type);
                            }
                        }
                        foreach (var p in iter.Outs) {
                            if (!p.IsGhost) {
                                CheckEqualityTypes_Type(p.tok, p.Type);
                            }
                        }
                        if (iter.Body != null) {
                            CheckEqualityTypes_Stmt(iter.Body);
                        }
                    } else if (d is ClassDecl) {
                        var cl = (ClassDecl)d;
                        foreach (var member in cl.Members) {
                            if (!member.IsGhost) {
                                if (member is Field) {
                                    var f = (Field)member;
                                    CheckEqualityTypes_Type(f.tok, f.Type);
                                } else if (member is Function) {
                                    var f = (Function)member;
                                    foreach (var p in f.Formals) {
                                        if (!p.IsGhost) {
                                            CheckEqualityTypes_Type(p.tok, p.Type);
                                        }
                                    }
                                    CheckEqualityTypes_Type(f.tok, f.ResultType);
                                    if (f.Body != null) {
                                        CheckEqualityTypes(f.Body);
                                    }
                                } else if (member is Method) {
                                    var m = (Method)member;
                                    foreach (var p in m.Ins) {
                                        if (!p.IsGhost) {
                                            CheckEqualityTypes_Type(p.tok, p.Type);
                                        }
                                    }
                                    foreach (var p in m.Outs) {
                                        if (!p.IsGhost) {
                                            CheckEqualityTypes_Type(p.tok, p.Type);
                                        }
                                    }
                                    if (m.Body != null) {
                                        CheckEqualityTypes_Stmt(m.Body);
                                    }
                                }
                            }
                        }
                    } else if (d is DatatypeDecl) {
                        var dt = (DatatypeDecl)d;
                        foreach (var ctor in dt.Ctors) {
                            foreach (var p in ctor.Formals) {
                                if (!p.IsGhost) {
                                    CheckEqualityTypes_Type(p.tok, p.Type);
                                }
                            }
                        }
                    }
                }
                // Check that copredicates are not recursive with non-copredicate functions.
                foreach (var fn in ModuleDefinition.AllFunctions(declarations)) {
                    if (fn.Body != null && (fn is CoPredicate || fn.IsRecursive)) {
                        CoPredicateChecks(fn.Body, fn, CallingPosition.Positive);
                    }
                }
            }
        }

        enum TailRecursionStatus {
            NotTailRecursive, // contains code that makes the enclosing method body not tail recursive (in way that is supported)
            CanBeFollowedByAnything, // the code just analyzed does not do any recursive calls
            TailCallSpent, // the method body is tail recursive, provided that all code that follows it in the method body is ghost
        }

        /// <summary>
        /// Checks if "stmts" can be considered tail recursive, and (provided "reportsError" is true) reports an error if not.
        /// Note, the current implementation is rather conservative in its analysis; upon need, the
        /// algorithm could be improved.
        /// In the current implementation, "enclosingMethod" is not allowed to be a mutually recursive method.
        /// 
        /// The incoming value of "tailCall" is not used, but it's nevertheless a 'ref' parameter to allow the
        /// body to return the incoming value or to omit assignments to it.
        /// If the return value is CanBeFollowedByAnything, "tailCall" is unchanged.
        /// If the return value is TailCallSpent, "tailCall" shows one of the calls where the tail call was spent.  (Note,
        /// there could be several if the statements have branches.)
        /// If the return value is NoTailRecursive, "tailCall" could be anything.  In this case, an error
        /// message has been reported (provided "reportsErrors" is true).
        /// </summary>
        TailRecursionStatus CheckTailRecursive(List<Statement> stmts, Method enclosingMethod, ref CallStmt tailCall, bool reportErrors) {
            Contract.Requires(stmts != null);
            var status = TailRecursionStatus.CanBeFollowedByAnything;
            foreach (var s in stmts) {
                if (!s.IsGhost) {
                    if (s is ReturnStmt && ((ReturnStmt)s).hiddenUpdate == null) {
                        return status;
                    }
                    if (status == TailRecursionStatus.TailCallSpent) {
                        // a tail call cannot be followed by non-ghost code
                        if (reportErrors) {
                            Error(tailCall.Tok, "this recursive call is not recognized as being tail recursive, because it is followed by non-ghost code");
                        }
                        return TailRecursionStatus.NotTailRecursive;
                    }
                    status = CheckTailRecursive(s, enclosingMethod, ref tailCall, reportErrors);
                    if (status == TailRecursionStatus.NotTailRecursive) {
                        return status;
                    }
                }
            }
            return status;
        }

        /// <summary>
        /// See CheckTailRecursive(List Statement, ...), including its description of "tailCall".
        /// In the current implementation, "enclosingMethod" is not allowed to be a mutually recursive method.
        /// </summary>
        TailRecursionStatus CheckTailRecursive(Statement stmt, Method enclosingMethod, ref CallStmt tailCall, bool reportErrors) {
            Contract.Requires(stmt != null && !stmt.IsGhost);
            if (stmt is PrintStmt) {
            } else if (stmt is BreakStmt) {
            } else if (stmt is ReturnStmt) {
                var s = (ReturnStmt)stmt;
                if (s.hiddenUpdate != null) {
                    return CheckTailRecursive(s.hiddenUpdate, enclosingMethod, ref tailCall, reportErrors);
                }
            } else if (stmt is AssignStmt) {
            } else if (stmt is VarDecl) {
            } else if (stmt is CallStmt) {
                var s = (CallStmt)stmt;
                if (s.Method == enclosingMethod) {
                    // It's a recursive call.  It can be considered a tail call only if the LHS of the call are the
                    // formal out-parameters of the method
                    for (int i = 0; i < s.Lhs.Count; i++) {
                        var formal = enclosingMethod.Outs[i];
                        if (!formal.IsGhost) {
                            var lhs = s.Lhs[i] as IdentifierExpr;
                            if (lhs != null && lhs.Var == formal) {
                                // all is good
                            } else {
                                if (reportErrors) {
                                    Error(s.Tok, "the recursive call to '{0}' is not tail recursive because the actual out-parameter {1} is not the formal out-parameter '{2}'", s.Method.Name, i, formal.Name);
                                }
                                return TailRecursionStatus.NotTailRecursive;
                            }
                        }
                    }
                    tailCall = s;
                    return TailRecursionStatus.TailCallSpent;
                }
            } else if (stmt is BlockStmt) {
                var s = (BlockStmt)stmt;
                return CheckTailRecursive(s.Body, enclosingMethod, ref tailCall, reportErrors);
            } else if (stmt is IfStmt) {
                var s = (IfStmt)stmt;
                var stThen = CheckTailRecursive(s.Thn, enclosingMethod, ref tailCall, reportErrors);
                if (stThen == TailRecursionStatus.NotTailRecursive) {
                    return stThen;
                }
                var stElse = s.Els == null ? TailRecursionStatus.CanBeFollowedByAnything : CheckTailRecursive(s.Els, enclosingMethod, ref tailCall, reportErrors);
                if (stElse == TailRecursionStatus.NotTailRecursive) {
                    return stElse;
                } else if (stThen == TailRecursionStatus.TailCallSpent || stElse == TailRecursionStatus.TailCallSpent) {
                    return TailRecursionStatus.TailCallSpent;
                }
            } else if (stmt is AlternativeStmt) {
                var s = (AlternativeStmt)stmt;
                var status = TailRecursionStatus.CanBeFollowedByAnything;
                foreach (var alt in s.Alternatives) {
                    var st = CheckTailRecursive(alt.Body, enclosingMethod, ref tailCall, reportErrors);
                    if (st == TailRecursionStatus.NotTailRecursive) {
                        return st;
                    } else if (st == TailRecursionStatus.TailCallSpent) {
                        status = st;
                    }
                }
                return status;
            } else if (stmt is WhileStmt) {
                var s = (WhileStmt)stmt;
                var status = CheckTailRecursive(s.Body, enclosingMethod, ref tailCall, reportErrors);
                if (status != TailRecursionStatus.CanBeFollowedByAnything) {
                    if (status == TailRecursionStatus.NotTailRecursive) {
                        // an error has already been reported
                    } else if (reportErrors) {
                        Error(tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call");
                    }
                    return TailRecursionStatus.NotTailRecursive;
                }
            } else if (stmt is AlternativeLoopStmt) {
                var s = (AlternativeLoopStmt)stmt;
                foreach (var alt in s.Alternatives) {
                    var status = CheckTailRecursive(alt.Body, enclosingMethod, ref tailCall, reportErrors);
                    if (status != TailRecursionStatus.CanBeFollowedByAnything) {
                        if (status == TailRecursionStatus.NotTailRecursive) {
                            // an error has already been reported
                        } else if (reportErrors) {
                            Error(tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call");
                        }
                        return TailRecursionStatus.NotTailRecursive;
                    }
                }
            } else if (stmt is ParallelStmt) {
                var s = (ParallelStmt)stmt;
                var status = CheckTailRecursive(s.Body, enclosingMethod, ref tailCall, reportErrors);
                if (status != TailRecursionStatus.CanBeFollowedByAnything) {
                    if (status == TailRecursionStatus.NotTailRecursive) {
                        // an error has already been reported
                    } else if (reportErrors) {
                        Error(tailCall.Tok, "a recursive call inside a parallel statement is not a tail call");
                    }
                    return TailRecursionStatus.NotTailRecursive;
                }
            } else if (stmt is MatchStmt) {
                var s = (MatchStmt)stmt;
                var status = TailRecursionStatus.CanBeFollowedByAnything;
                foreach (var kase in s.Cases) {
                    var st = CheckTailRecursive(kase.Body, enclosingMethod, ref tailCall, reportErrors);
                    if (st == TailRecursionStatus.NotTailRecursive) {
                        return st;
                    } else if (st == TailRecursionStatus.TailCallSpent) {
                        status = st;
                    }
                }
                return status;
            } else if (stmt is AssignSuchThatStmt) {
            } else if (stmt is ConcreteSyntaxStatement) {
                var s = (ConcreteSyntaxStatement)stmt;
                return CheckTailRecursive(s.ResolvedStatements, enclosingMethod, ref tailCall, reportErrors);
            } else {
                Contract.Assert(false);  // unexpected statement type
            }
            return TailRecursionStatus.CanBeFollowedByAnything;
        }

        enum CallingPosition { Positive, Negative, Neither }

        static CallingPosition Invert(CallingPosition cp) {
            switch (cp) {
                case CallingPosition.Positive: return CallingPosition.Negative;
                case CallingPosition.Negative: return CallingPosition.Positive;
                default: return CallingPosition.Neither;
            }
        }

        void CoPredicateChecks(Expression expr, Function context, CallingPosition cp) {
            Contract.Requires(expr != null);
            Contract.Requires(context != null);
            if (expr is ConcreteSyntaxExpression) {
                var e = (ConcreteSyntaxExpression)expr;
                CoPredicateChecks(e.Resolved, context, cp);
                return;
            } else if (expr is FunctionCallExpr) {
                var e = (FunctionCallExpr)expr;
                var moduleCaller = context.EnclosingClass.Module;
                var moduleCallee = e.Function.EnclosingClass.Module;
                if (moduleCaller == moduleCallee && moduleCaller.CallGraph.GetSCCRepresentative(context) == moduleCaller.CallGraph.GetSCCRepresentative(e.Function)) {
                    // we're looking at a recursive call
                    if (context is CoPredicate) {
                        if (!(e.Function is CoPredicate)) {
                            Error(e, "a recursive call from a copredicate can go only to other copredicates");
                        } else if (cp != CallingPosition.Positive) {
                            Error(e, "a recursive copredicate call can only be done in positive positions");
                        }
                    } else if (e.Function is CoPredicate) {
                        Error(e, "a recursive call from a non-copredicate can go only to other non-copredicates");
                    }
                }
                // fall through to do the subexpressions
            } else if (expr is UnaryExpr) {
                var e = (UnaryExpr)expr;
                if (e.Op == UnaryExpr.Opcode.Not) {
                    CoPredicateChecks(e.E, context, Invert(cp));
                    return;
                }
            } else if (expr is BinaryExpr) {
                var e = (BinaryExpr)expr;
                switch (e.ResolvedOp) {
                    case BinaryExpr.ResolvedOpcode.And:
                    case BinaryExpr.ResolvedOpcode.Or:
                        CoPredicateChecks(e.E0, context, cp);
                        CoPredicateChecks(e.E1, context, cp);
                        return;
                    case BinaryExpr.ResolvedOpcode.Imp:
                        CoPredicateChecks(e.E0, context, Invert(cp));
                        CoPredicateChecks(e.E1, context, cp);
                        return;
                    default:
                        break;
                }
            } else if (expr is MatchExpr) {
                var e = (MatchExpr)expr;
                CoPredicateChecks(e.Source, context, CallingPosition.Neither);
                e.Cases.Iter(kase => CoPredicateChecks(kase.Body, context, cp));
                return;
            } else if (expr is ITEExpr) {
                var e = (ITEExpr)expr;
                CoPredicateChecks(e.Test, context, CallingPosition.Neither);
                CoPredicateChecks(e.Thn, context, cp);
                CoPredicateChecks(e.Els, context, cp);
            } else if (expr is LetExpr) {
                var e = (LetExpr)expr;
                CoPredicateChecks(e.Body, context, cp);
                return;
            } else if (expr is QuantifierExpr) {
                var e = (QuantifierExpr)expr;
                if (e.Range != null) {
                    CoPredicateChecks(e.Range, context, e is ExistsExpr ? Invert(cp) : cp);
                }
                CoPredicateChecks(e.Term, context, cp);
                return;
            }
            expr.SubExpressions.Iter(ee => CoPredicateChecks(ee, context, CallingPosition.Neither));
        }

        void CheckEqualityTypes_Stmt(Statement stmt) {
            Contract.Requires(stmt != null);
            if (stmt.IsGhost) {
                return;
            } else if (stmt is PrintStmt) {
                var s = (PrintStmt)stmt;
                foreach (var arg in s.Args) {
                    if (arg.E != null) {
                        CheckEqualityTypes(arg.E);
                    }
                }
            } else if (stmt is BreakStmt) {
            } else if (stmt is ProduceStmt) {
                var s = (ProduceStmt)stmt;
                if (s.rhss != null) {
                    s.rhss.Iter(CheckEqualityTypes_Rhs);
                }
            } else if (stmt is AssignStmt) {
                AssignStmt s = (AssignStmt)stmt;
                CheckEqualityTypes(s.Lhs);
                CheckEqualityTypes_Rhs(s.Rhs);
            } else if (stmt is VarDecl) {
                var s = (VarDecl)stmt;
                s.SubStatements.Iter(CheckEqualityTypes_Stmt);
            } else if (stmt is CallStmt) {
                var s = (CallStmt)stmt;
                CheckEqualityTypes(s.Receiver);
                s.Args.Iter(CheckEqualityTypes);
                s.Lhs.Iter(CheckEqualityTypes);

                Contract.Assert(s.Method.TypeArgs.Count <= s.TypeArgumentSubstitutions.Count);
                var i = 0;
                foreach (var formalTypeArg in s.Method.TypeArgs) {
                    var actualTypeArg = s.TypeArgumentSubstitutions[formalTypeArg];
                    if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) {
                        Error(s.Tok, "type parameter {0} ({1}) passed to method {2} must support equality (got {3}){4}", i, formalTypeArg.Name, s.Method.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
                    }
                    i++;
                }
            } else if (stmt is BlockStmt) {
                var s = (BlockStmt)stmt;
                s.Body.Iter(CheckEqualityTypes_Stmt);
            } else if (stmt is IfStmt) {
                var s = (IfStmt)stmt;
                if (s.Guard != null) {
                    CheckEqualityTypes(s.Guard);
                }
                s.SubStatements.Iter(CheckEqualityTypes_Stmt);
            } else if (stmt is AlternativeStmt) {
                var s = (AlternativeStmt)stmt;
                foreach (var alt in s.Alternatives) {
                    CheckEqualityTypes(alt.Guard);
                    alt.Body.Iter(CheckEqualityTypes_Stmt);
                }
            } else if (stmt is WhileStmt) {
                var s = (WhileStmt)stmt;
                if (s.Guard != null) {
                    CheckEqualityTypes(s.Guard);
                }
                CheckEqualityTypes_Stmt(s.Body);
            } else if (stmt is AlternativeLoopStmt) {
                var s = (AlternativeLoopStmt)stmt;
                foreach (var alt in s.Alternatives) {
                    CheckEqualityTypes(alt.Guard);
                    alt.Body.Iter(CheckEqualityTypes_Stmt);
                }
            } else if (stmt is ParallelStmt) {
                var s = (ParallelStmt)stmt;
                CheckEqualityTypes(s.Range);
                CheckEqualityTypes_Stmt(s.Body);
            } else if (stmt is MatchStmt) {
                var s = (MatchStmt)stmt;
                CheckEqualityTypes(s.Source);
                foreach (MatchCaseStmt mc in s.Cases) {
                    mc.Body.Iter(CheckEqualityTypes_Stmt);
                }
            } else if (stmt is ConcreteSyntaxStatement) {
                var s = (ConcreteSyntaxStatement)stmt;
                s.ResolvedStatements.Iter(CheckEqualityTypes_Stmt);
            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected statement
            }
        }

        void CheckEqualityTypes_Rhs(AssignmentRhs rhs) {
            Contract.Requires(rhs != null);
            rhs.SubExpressions.Iter(CheckEqualityTypes);
            rhs.SubStatements.Iter(CheckEqualityTypes_Stmt);
        }

        void CheckEqualityTypes(Expression expr) {
            Contract.Requires(expr != null);
            if (expr is BinaryExpr) {
                var e = (BinaryExpr)expr;
                var t0 = e.E0.Type.Normalize();
                var t1 = e.E1.Type.Normalize();
                switch (e.Op) {
                    case BinaryExpr.Opcode.Eq:
                    case BinaryExpr.Opcode.Neq:
                        // First, check a special case:  a datatype value (like Nil) that takes no parameters
                        var e0 = e.E0.Resolved as DatatypeValue;
                        var e1 = e.E1.Resolved as DatatypeValue;
                        if (e0 != null && e0.Arguments.Count == 0) {
                            // that's cool
                        } else if (e1 != null && e1.Arguments.Count == 0) {
                            // oh yeah!
                        } else if (!t0.SupportsEquality) {
                            Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
                        } else if (!t1.SupportsEquality) {
                            Error(e.E1, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
                        }
                        break;
                    default:
                        switch (e.ResolvedOp) {
                            // Note, all operations on sets, multisets, and maps are guaranteed to work because of restrictions placed on how
                            // these types are instantiated.  (Except: This guarantee does not apply to equality on maps, because the Range type
                            // of maps is not restricted, only the Domain type.  However, the equality operator is checked above.)
                            case BinaryExpr.ResolvedOpcode.InSeq:
                            case BinaryExpr.ResolvedOpcode.NotInSeq:
                            case BinaryExpr.ResolvedOpcode.Prefix:
                            case BinaryExpr.ResolvedOpcode.ProperPrefix:
                                if (!t1.SupportsEquality) {
                                    Error(e.E1, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1));
                                } else if (!t0.SupportsEquality) {
                                    if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.InSet || e.ResolvedOp == BinaryExpr.ResolvedOpcode.NotInSeq) {
                                        Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
                                    } else {
                                        Error(e.E0, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0));
                                    }
                                }
                                break;
                            default:
                                break;
                        }
                        break;
                }
            } else if (expr is ComprehensionExpr) {
                var e = (ComprehensionExpr)expr;
                foreach (var bv in e.BoundVars) {
                    CheckEqualityTypes_Type(bv.tok, bv.Type);
                }
            } else if (expr is LetExpr) {
                var e = (LetExpr)expr;
                foreach (var bv in e.Vars) {
                    CheckEqualityTypes_Type(bv.tok, bv.Type);
                }
            } else if (expr is FunctionCallExpr) {
                var e = (FunctionCallExpr)expr;
                Contract.Assert(e.Function.TypeArgs.Count <= e.TypeArgumentSubstitutions.Count);
                var i = 0;
                foreach (var formalTypeArg in e.Function.TypeArgs) {
                    var actualTypeArg = e.TypeArgumentSubstitutions[formalTypeArg];
                    if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) {
                        Error(e.tok, "type parameter {0} ({1}) passed to function {2} must support equality (got {3}){4}", i, formalTypeArg.Name, e.Function.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg));
                    }
                    i++;
                }
            }

            foreach (var ee in expr.SubExpressions) {
                CheckEqualityTypes(ee);
            }
        }

        void CheckEqualityTypes_Type(IToken tok, Type type) {
            Contract.Requires(tok != null);
            Contract.Requires(type != null);
            type = type.Normalize();
            if (type is BasicType) {
                // fine
            } else if (type is SetType) {
                var argType = ((SetType)type).Arg;
                if (!argType.SupportsEquality) {
                    Error(tok, "set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
                }
                CheckEqualityTypes_Type(tok, argType);

            } else if (type is MultiSetType) {
                var argType = ((MultiSetType)type).Arg;
                if (!argType.SupportsEquality) {
                    Error(tok, "multiset argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType));
                }
                CheckEqualityTypes_Type(tok, argType);

            } else if (type is MapType) {
                var mt = (MapType)type;
                if (!mt.Domain.SupportsEquality) {
                    Error(tok, "map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain));
                }
                CheckEqualityTypes_Type(tok, mt.Domain);
                CheckEqualityTypes_Type(tok, mt.Range);

            } else if (type is SeqType) {
                Type argType = ((SeqType)type).Arg;
                CheckEqualityTypes_Type(tok, argType);

            } else if (type is UserDefinedType) {
                var udt = (UserDefinedType)type;
                if (udt.ResolvedClass != null) {
                    Contract.Assert(udt.ResolvedClass.TypeArgs.Count == udt.TypeArgs.Count);
                    var i = 0;
                    foreach (var argType in udt.TypeArgs) {
                        var formalTypeArg = udt.ResolvedClass.TypeArgs[i];
                        if (formalTypeArg.MustSupportEquality && !argType.SupportsEquality) {
                            Error(tok, "type parameter {0} ({1}) passed to type {2} must support equality (got {3}){4}", i, formalTypeArg.Name, udt.ResolvedClass.Name, argType, TypeEqualityErrorMessageHint(argType));
                        }
                        CheckEqualityTypes_Type(tok, argType);
                        i++;
                    }
                } else {
                    Contract.Assert(udt.TypeArgs.Count == 0);  // TypeParameters have no type arguments
                }

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected type
            }
        }

        bool CheckTypeInference(Expression e) {
            if (e == null) return false;
            foreach (Expression se in e.SubExpressions) {
                if (CheckTypeInference(se))
                    return true;
            }
            if (e.Type is TypeProxy && !(e.Type is InferredTypeProxy || e.Type is ParamTypeProxy || e.Type is ObjectTypeProxy)) {
                Error(e.tok, "the type of this expression is underspecified, but it cannot be an arbitrary type.");
                return true;
            }
            return false;
        }
        void CheckTypeInference(Statement stmt) {
            Contract.Requires(stmt != null);
            if (stmt is PrintStmt) {
                var s = (PrintStmt)stmt;
                s.Args.Iter(arg => CheckTypeInference(arg.E));
            } else if (stmt is BreakStmt) {
            } else if (stmt is ProduceStmt) {
                var s = (ProduceStmt)stmt;
                if (s.rhss != null) {
                    s.rhss.Iter(rhs => rhs.SubExpressions.Iter(e => CheckTypeInference(e)));
                }
            } else if (stmt is AssignStmt) {
                AssignStmt s = (AssignStmt)stmt;
                CheckTypeInference(s.Lhs);
                s.Rhs.SubExpressions.Iter(e => { CheckTypeInference(e); });
            } else if (stmt is VarDecl) {
                var s = (VarDecl)stmt;
                s.SubStatements.Iter(CheckTypeInference);
                if (s.Type is TypeProxy && !(s.Type is InferredTypeProxy || s.Type is ParamTypeProxy || s.Type is ObjectTypeProxy)) {
                    Error(s.Tok, "the type of this expression is underspecified, but it cannot be an arbitrary type.");
                }
            } else if (stmt is CallStmt) {
                var s = (CallStmt)stmt;
                CheckTypeInference(s.Receiver);
                s.Args.Iter(e => CheckTypeInference(e));
                s.Lhs.Iter(e => CheckTypeInference(e));
            } else if (stmt is BlockStmt) {
                var s = (BlockStmt)stmt;
                s.Body.Iter(CheckTypeInference);
            } else if (stmt is IfStmt) {
                var s = (IfStmt)stmt;
                if (s.Guard != null) {
                    CheckTypeInference(s.Guard);
                }
                s.SubStatements.Iter(CheckTypeInference);
            } else if (stmt is AlternativeStmt) {
                var s = (AlternativeStmt)stmt;
                foreach (var alt in s.Alternatives) {
                    CheckTypeInference(alt.Guard);
                    alt.Body.Iter(CheckTypeInference);
                }
            } else if (stmt is WhileStmt) {
                var s = (WhileStmt)stmt;
                if (s.Guard != null) {
                    CheckTypeInference(s.Guard);
                }
                CheckTypeInference(s.Body);
            } else if (stmt is AlternativeLoopStmt) {
                var s = (AlternativeLoopStmt)stmt;
                foreach (var alt in s.Alternatives) {
                    CheckTypeInference(alt.Guard);
                    alt.Body.Iter(CheckTypeInference);
                }
            } else if (stmt is ParallelStmt) {
                var s = (ParallelStmt)stmt;
                CheckTypeInference(s.Range);
                CheckTypeInference(s.Body);
            } else if (stmt is CalcStmt) {
                // NadiaToDo: is this correct?
                var s = (CalcStmt)stmt;
                s.SubExpressions.Iter(e => CheckTypeInference(e));
                s.SubStatements.Iter(CheckTypeInference);
            } else if (stmt is MatchStmt) {
                var s = (MatchStmt)stmt;
                CheckTypeInference(s.Source);
                foreach (MatchCaseStmt mc in s.Cases) {
                    mc.Body.Iter(CheckTypeInference);
                }
            } else if (stmt is ConcreteSyntaxStatement) {
                var s = (ConcreteSyntaxStatement)stmt;
                s.ResolvedStatements.Iter(CheckTypeInference);
            } else if (stmt is PredicateStmt) {
                CheckTypeInference(((PredicateStmt)stmt).Expr);
            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected statement
            }
        }

        string TypeEqualityErrorMessageHint(Type argType) {
            Contract.Requires(argType != null);
            var tp = argType.AsTypeParameter;
            if (tp != null) {
                return string.Format(" (perhaps try declaring type parameter '{0}' on line {1} as '{0}(==)', which says it can only be instantiated with a type that supports equality)", tp.Name, tp.tok.line);
            }
            return "";
        }

        bool InferRequiredEqualitySupport(TypeParameter tp, Type type) {
            Contract.Requires(tp != null);
            Contract.Requires(type != null);

            type = type.Normalize();
            if (type is BasicType) {
            } else if (type is SetType) {
                var st = (SetType)type;
                return st.Arg.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, st.Arg);
            } else if (type is MultiSetType) {
                var ms = (MultiSetType)type;
                return ms.Arg.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, ms.Arg);
            } else if (type is MapType) {
                var mt = (MapType)type;
                return mt.Domain.AsTypeParameter == tp || InferRequiredEqualitySupport(tp, mt.Domain) || InferRequiredEqualitySupport(tp, mt.Range);
            } else if (type is SeqType) {
                var sq = (SeqType)type;
                return InferRequiredEqualitySupport(tp, sq.Arg);
            } else if (type is UserDefinedType) {
                var udt = (UserDefinedType)type;
                if (udt.ResolvedClass != null) {
                    var i = 0;
                    foreach (var argType in udt.TypeArgs) {
                        var formalTypeArg = udt.ResolvedClass.TypeArgs[i];
                        if ((formalTypeArg.MustSupportEquality && argType.AsTypeParameter == tp) || InferRequiredEqualitySupport(tp, argType)) {
                            return true;
                        }
                        i++;
                    }
                } else {
                    Contract.Assert(udt.TypeArgs.Count == 0);  // TypeParameters have no type arguments
                }
            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected type
            }
            return false;
        }

        ClassDecl currentClass;
        Function currentFunction;
        readonly Scope<TypeParameter>/*!*/ allTypeParameters = new Scope<TypeParameter>();
        readonly Scope<IVariable>/*!*/ scope = new Scope<IVariable>();
        Scope<Statement>/*!*/ labeledStatements = new Scope<Statement>();
        List<Statement> loopStack = new List<Statement>();  // the enclosing loops (from which it is possible to break out)
        readonly Dictionary<Statement, bool> inSpecOnlyContext = new Dictionary<Statement, bool>();  // invariant: domain contain union of the domains of "labeledStatements" and "loopStack"


        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveClassMemberTypes(ClassDecl/*!*/ cl) {
            Contract.Requires(cl != null);
            Contract.Requires(currentClass == null);
            Contract.Ensures(currentClass == null);

            currentClass = cl;
            foreach (MemberDecl member in cl.Members) {
                member.EnclosingClass = cl;
                if (member is Field) {
                    ResolveType(member.tok, ((Field)member).Type, null, false);

                } else if (member is Function) {
                    Function f = (Function)member;
                    allTypeParameters.PushMarker();
                    ResolveTypeParameters(f.TypeArgs, true, f);
                    ResolveFunctionSignature(f);
                    allTypeParameters.PopMarker();

                } else if (member is Method) {
                    Method m = (Method)member;
                    allTypeParameters.PushMarker();
                    ResolveTypeParameters(m.TypeArgs, true, m);
                    ResolveMethodSignature(m);
                    allTypeParameters.PopMarker();

                } else {
                    Contract.Assert(false); throw new cce.UnreachableException();  // unexpected member type
                }
            }
            currentClass = null;
        }

        /// <summary>
        /// Assumes type parameters have already been pushed, and that all types in class members have been resolved
        /// </summary>
        void ResolveClassMemberBodies(ClassDecl cl) {
            Contract.Requires(cl != null);
            Contract.Requires(currentClass == null);
            Contract.Ensures(currentClass == null);

            currentClass = cl;
            foreach (MemberDecl member in cl.Members) {
                if (member is Field) {
                    ResolveAttributes(member.Attributes, false);
                    // nothing more to do

                } else if (member is Function) {
                    Function f = (Function)member;
                    allTypeParameters.PushMarker();
                    ResolveTypeParameters(f.TypeArgs, false, f);
                    ResolveFunction(f);
                    allTypeParameters.PopMarker();

                } else if (member is Method) {
                    Method m = (Method)member;
                    allTypeParameters.PushMarker();
                    ResolveTypeParameters(m.TypeArgs, false, m);
                    ResolveMethod(m);
                    allTypeParameters.PopMarker();
                } else {
                    Contract.Assert(false); throw new cce.UnreachableException();  // unexpected member type
                }
            }
            currentClass = null;
        }

        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveCtorTypes(DatatypeDecl/*!*/ dt, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
            Contract.Requires(dt != null);
            Contract.Requires(dependencies != null);  // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));
            foreach (DatatypeCtor ctor in dt.Ctors) {

                ctor.EnclosingDatatype = dt;

                allTypeParameters.PushMarker();
                ResolveCtorSignature(ctor, dt.TypeArgs);
                allTypeParameters.PopMarker();

                if (dt is IndDatatypeDecl) {
                    var idt = (IndDatatypeDecl)dt;
                    dependencies.AddVertex(idt);
                    foreach (Formal p in ctor.Formals) {
                        AddDatatypeDependencyEdge(idt, p.Type, dependencies);
                    }
                }
            }
        }

        void AddDatatypeDependencyEdge(IndDatatypeDecl/*!*/ dt, Type/*!*/ tp, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
            Contract.Requires(dt != null);
            Contract.Requires(tp != null);
            Contract.Requires(dependencies != null);  // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));

            var dependee = tp.AsIndDatatype;
            if (dependee != null && dt.Module == dependee.Module) {
                dependencies.AddEdge((IndDatatypeDecl)dt, dependee);
                foreach (var ta in ((UserDefinedType)tp).TypeArgs) {
                    AddDatatypeDependencyEdge(dt, ta, dependencies);
                }
            }
        }

        /// <summary>
        /// Check that the SCC of 'startingPoint' can be carved up into stratospheres in such a way that each
        /// datatype has some value that can be constructed from datatypes in lower stratospheres only.
        /// The algorithm used here is quadratic in the number of datatypes in the SCC.  Since that number is
        /// deemed to be rather small, this seems okay.
        /// 
        /// As a side effect of this checking, the DefaultCtor field is filled in (for every inductive datatype
        /// that passes the check).  It may be that several constructors could be used as the default, but
        /// only the first one encountered as recorded.  This particular choice is slightly more than an
        /// implementation detail, because it affects how certain cycles among inductive datatypes (having
        /// to do with the types used to instantiate type parameters of datatypes) are used.
        /// 
        /// The role of the SCC here is simply to speed up this method.  It would still be correct if the
        /// equivalence classes in the given SCC were unions of actual SCC's.  In particular, this method
        /// would still work if "dependencies" consisted of one large SCC containing all the inductive
        /// datatypes in the module.
        /// </summary>
        void SccStratosphereCheck(IndDatatypeDecl startingPoint, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
            Contract.Requires(startingPoint != null);
            Contract.Requires(dependencies != null);  // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));

            var scc = dependencies.GetSCC(startingPoint);
            int totalCleared = 0;
            while (true) {
                int clearedThisRound = 0;
                foreach (var dt in scc) {
                    if (dt.DefaultCtor != null) {
                        // previously cleared
                    } else if (ComputeDefaultCtor(dt)) {
                        Contract.Assert(dt.DefaultCtor != null);  // should have been set by the successful call to StratosphereCheck)
                        clearedThisRound++;
                        totalCleared++;
                    }
                }
                if (totalCleared == scc.Count) {
                    // all is good
                    return;
                } else if (clearedThisRound != 0) {
                    // some progress was made, so let's keep going
                } else {
                    // whatever is in scc-cleared now failed to pass the test
                    foreach (var dt in scc) {
                        if (dt.DefaultCtor == null) {
                            Error(dt, "because of cyclic dependencies among constructor argument types, no instances of datatype '{0}' can be constructed", dt.Name);
                        }
                    }
                    return;
                }
            }
        }

        /// <summary>
        /// Check that the datatype has some constructor all whose argument types can be constructed.
        /// Returns 'true' and sets dt.DefaultCtor if that is the case.
        /// </summary>
        bool ComputeDefaultCtor(IndDatatypeDecl dt) {
            Contract.Requires(dt != null);
            Contract.Requires(dt.DefaultCtor == null);  // the intention is that this method be called only when DefaultCtor hasn't already been set
            Contract.Ensures(!Contract.Result<bool>() || dt.DefaultCtor != null);

            // Stated differently, check that there is some constuctor where no argument type goes to the same stratum.
            foreach (DatatypeCtor ctor in dt.Ctors) {
                var typeParametersUsed = new List<TypeParameter>();
                foreach (Formal p in ctor.Formals) {
                    if (!CheckCanBeConstructed(p.Type, typeParametersUsed)) {
                        // the argument type (has a component which) is not yet known to be constructable
                        goto NEXT_OUTER_ITERATION;
                    }
                }
                // this constructor satisfies the requirements, so the datatype is allowed
                dt.DefaultCtor = ctor;
                dt.TypeParametersUsedInConstructionByDefaultCtor = new bool[dt.TypeArgs.Count];
                for (int i = 0; i < dt.TypeArgs.Count; i++) {
                    dt.TypeParametersUsedInConstructionByDefaultCtor[i] = typeParametersUsed.Contains(dt.TypeArgs[i]);
                }
                return true;
            NEXT_OUTER_ITERATION: { }
            }
            // no constructor satisfied the requirements, so this is an illegal datatype declaration
            return false;
        }

        bool CheckCanBeConstructed(Type tp, List<TypeParameter> typeParametersUsed) {
            var dependee = tp.AsIndDatatype;
            if (dependee == null) {
                // the type is not an inductive datatype, which means it is always possible to construct it
                if (tp.IsTypeParameter) {
                    typeParametersUsed.Add(((UserDefinedType)tp).ResolvedParam);
                }
                return true;
            } else if (dependee.DefaultCtor == null) {
                // the type is an inductive datatype that we don't yet know how to construct
                return false;
            }
            // also check the type arguments of the inductive datatype
            Contract.Assert(((UserDefinedType)tp).TypeArgs.Count == dependee.TypeParametersUsedInConstructionByDefaultCtor.Length);
            var i = 0;
            foreach (var ta in ((UserDefinedType)tp).TypeArgs) {  // note, "tp" is known to be a UserDefinedType, because that follows from tp being an inductive datatype
                if (dependee.TypeParametersUsedInConstructionByDefaultCtor[i] && !CheckCanBeConstructed(ta, typeParametersUsed)) {
                    return false;
                }
                i++;
            }
            return true;
        }

        void DetermineEqualitySupport(IndDatatypeDecl startingPoint, Graph<IndDatatypeDecl/*!*/>/*!*/ dependencies) {
            Contract.Requires(startingPoint != null);
            Contract.Requires(dependencies != null);  // more expensive check: Contract.Requires(cce.NonNullElements(dependencies));

            var scc = dependencies.GetSCC(startingPoint);
            // First, the simple case:  If any parameter of any inductive datatype in the SCC is of a codatatype type, then
            // the whole SCC is incapable of providing the equality operation.
            foreach (var dt in scc) {
                Contract.Assume(dt.EqualitySupport == IndDatatypeDecl.ES.NotYetComputed);
                foreach (var ctor in dt.Ctors) {
                    foreach (var arg in ctor.Formals) {
                        var anotherIndDt = arg.Type.AsIndDatatype;
                        if ((anotherIndDt != null && anotherIndDt.EqualitySupport == IndDatatypeDecl.ES.Never) || arg.Type.IsCoDatatype) {
                            // arg.Type is known never to support equality
                            // So, go around the entire SCC and record what we learnt
                            foreach (var ddtt in scc) {
                                ddtt.EqualitySupport = IndDatatypeDecl.ES.Never;
                            }
                            return;  // we are done
                        }
                    }
                }
            }

            // Now for the more involved case:  we need to determine which type parameters determine equality support for each datatype in the SCC
            // We start by seeing where each datatype's type parameters are used in a place known to determine equality support.
            bool thingsChanged = false;
            foreach (var dt in scc) {
                if (dt.TypeArgs.Count == 0) {
                    // if the datatype has no type parameters, we certainly won't find any type parameters being used in the arguments types to the constructors
                    continue;
                }
                foreach (var ctor in dt.Ctors) {
                    foreach (var arg in ctor.Formals) {
                        var typeArg = arg.Type.AsTypeParameter;
                        if (typeArg != null) {
                            typeArg.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
                            thingsChanged = true;
                        } else {
                            var otherDt = arg.Type.AsIndDatatype;
                            if (otherDt != null && otherDt.EqualitySupport == IndDatatypeDecl.ES.ConsultTypeArguments) {  // datatype is in a different SCC
                                var otherUdt = (UserDefinedType)arg.Type.Normalize();
                                var i = 0;
                                foreach (var otherTp in otherDt.TypeArgs) {
                                    if (otherTp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
                                        var tp = otherUdt.TypeArgs[i].AsTypeParameter;
                                        if (tp != null) {
                                            tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
                                            thingsChanged = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // Then we propagate this information up through the SCC
            while (thingsChanged) {
                thingsChanged = false;
                foreach (var dt in scc) {
                    if (dt.TypeArgs.Count == 0) {
                        // if the datatype has no type parameters, we certainly won't find any type parameters being used in the arguments types to the constructors
                        continue;
                    }
                    foreach (var ctor in dt.Ctors) {
                        foreach (var arg in ctor.Formals) {
                            var otherDt = arg.Type.AsIndDatatype;
                            if (otherDt != null && otherDt.EqualitySupport == IndDatatypeDecl.ES.NotYetComputed) { // otherDt lives in the same SCC
                                var otherUdt = (UserDefinedType)arg.Type.Normalize();
                                var i = 0;
                                foreach (var otherTp in otherDt.TypeArgs) {
                                    if (otherTp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
                                        var tp = otherUdt.TypeArgs[i].AsTypeParameter;
                                        if (tp != null && !tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype) {
                                            tp.NecessaryForEqualitySupportOfSurroundingInductiveDatatype = true;
                                            thingsChanged = true;
                                        }
                                    }
                                    i++;
                                }
                            }
                        }
                    }
                }
            }
            // Now that we have computed the .NecessaryForEqualitySupportOfSurroundingInductiveDatatype values, mark the datatypes as ones
            // where equality support should be checked by looking at the type arguments.
            foreach (var dt in scc) {
                dt.EqualitySupport = IndDatatypeDecl.ES.ConsultTypeArguments;
            }
        }

        void ResolveAttributes(Attributes attrs, bool twoState) {
            // order does not matter much for resolution, so resolve them in reverse order
            for (; attrs != null; attrs = attrs.Prev) {
                if (attrs.Args != null) {
                    ResolveAttributeArgs(attrs.Args, twoState, true);
                }
            }
        }

        void ResolveAttributeArgs(List<Attributes.Argument/*!*/>/*!*/ args, bool twoState, bool allowGhosts) {
            Contract.Requires(args != null);
            foreach (Attributes.Argument aa in args) {
                Contract.Assert(aa != null);
                if (aa.E != null) {
                    ResolveExpression(aa.E, twoState);
                    if (!allowGhosts) {
                        CheckIsNonGhost(aa.E);
                    }
                }
            }
        }

        void ResolveTypeParameters(List<TypeParameter/*!*/>/*!*/ tparams, bool emitErrors, TypeParameter.ParentType/*!*/ parent) {
            Contract.Requires(tparams != null);
            Contract.Requires(parent != null);
            // push non-duplicated type parameter names
            int index = 0;
            foreach (TypeParameter tp in tparams) {
                Contract.Assert(tp != null);
                if (emitErrors) {
                    // we're seeing this TypeParameter for the first time
                    tp.Parent = parent;
                    tp.PositionalIndex = index;
                }
                if (!allTypeParameters.Push(tp.Name, tp) && emitErrors) {
                    Error(tp, "Duplicate type-parameter name: {0}", tp.Name);
                }
            }
        }

        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveFunctionSignature(Function f) {
            Contract.Requires(f != null);
            scope.PushMarker();
            if (f.SignatureIsOmitted) {
                Error(f, "function signature can be omitted only in refining functions");
            }
            var defaultTypeArguments = f.TypeArgs.Count == 0 ? f.TypeArgs : null;
            foreach (Formal p in f.Formals) {
                if (!scope.Push(p.Name, p)) {
                    Error(p, "Duplicate parameter name: {0}", p.Name);
                }
                ResolveType(p.tok, p.Type, defaultTypeArguments, true);
            }
            ResolveType(f.tok, f.ResultType, defaultTypeArguments, true);
            scope.PopMarker();
        }

        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveFunction(Function f) {
            Contract.Requires(f != null);
            Contract.Requires(currentFunction == null);
            Contract.Ensures(currentFunction == null);
            scope.PushMarker();
            currentFunction = f;
            if (f.IsStatic) {
                scope.AllowInstance = false;
            }
            foreach (Formal p in f.Formals) {
                scope.Push(p.Name, p);
            }
            ResolveAttributes(f.Attributes, false);
            foreach (Expression r in f.Req) {
                ResolveExpression(r, false);
                Contract.Assert(r.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(r.Type, Type.Bool)) {
                    Error(r, "Precondition must be a boolean (got {0})", r.Type);
                }
            }
            foreach (FrameExpression fr in f.Reads) {
                ResolveFrameExpression(fr, "reads");
            }
            foreach (Expression r in f.Ens) {
                ResolveExpression(r, false);  // since this is a function, the postcondition is still a one-state predicate
                Contract.Assert(r.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(r.Type, Type.Bool)) {
                    Error(r, "Postcondition must be a boolean (got {0})", r.Type);
                }
            }
            foreach (Expression r in f.Decreases.Expressions) {
                ResolveExpression(r, false);
                // any type is fine
            }
            if (f.Body != null) {
                var prevErrorCount = ErrorCount;
                List<IVariable> matchVarContext = new List<IVariable>(f.Formals);
                ResolveExpression(f.Body, false, matchVarContext);
                if (!f.IsGhost) {
                    CheckIsNonGhost(f.Body);
                }
                Contract.Assert(f.Body.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(f.Body.Type, f.ResultType)) {
                    Error(f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type);
                }
            }
            currentFunction = null;
            scope.PopMarker();
        }

        // Yuyan 
        // resolve expression in the form of region expression
        // o <==> region{o.*}
        // this <==> region{this.*}
        // o.f <==> region{o.f}
        // TBD: collection type
        void ResolveRegionExpression(RegionConstructExpression rce) {
            Contract.Requires(rce != null);
            Contract.Ensures(Contract.Result<List<Region>>() != null);

            rce.Type = new RegionType(); 
            Expression e = rce.E;

            Type t = e.Type;
            Contract.Assert(t != null);  // follows from postcondition of ResolveExpression
            if (e.Resolved is LiteralExpr) {  // for region{}

            } else if (e.Resolved is WildcardExpr) {
                // nothing to do
            } else if (e.Resolved is FieldSelectExpr) { // region{o.f} 
                var fse = (FieldSelectExpr)e.Resolved;
                t = fse.Obj.Type;
                if (fse.Field.Name == "*") {
                    rce.RegionList = GenerateRegionList(fse.Obj);
                    rce.canUseRThis = false;
                } else {
                    rce.RegionList.Add(new Region(fse.Obj, fse.Field));
                }
            } else if (e.Resolved is ThisExpr) { // region{this} or region{o}
                // use axiom RegionOfRef instead of enumerating all the fileds
                // regionList.Add(new Region(e.Resolved, new Field(Token.NoToken, "*", false, Type.Flexible, null)));
                rce.RegionList = GenerateRegionList(e);
                rce.canUseRThis = false;
            } else if (e.Resolved is IdentifierExpr) {
                rce.RegionList = GenerateRegionList(e);
                rce.canUseRThis = false;    // sometimes not sure what rthis is. we cannot use rthis to generate region
            } else if (e.Resolved is FunctionCallExpr) {
                //***** Cannot resovle it here. rce.RegionLisg with the type List<Region> cannot represent IdentifierExpr *****//
                //FunctionCallExpr f = (FunctionCallExpr)e.Resolved;
                //Contract.Assert(f.Function != null); // follows from the fact that expr has been successfully resolved
                //// create a local variable for each formal parameter, and assign each actual parameter to the corresponding local
                //Dictionary<IVariable, Expression> substMap = new Dictionary<IVariable, Expression>();
                //for (int i = 0; i < f.Function.Formals.Count; i++) {
                //    substMap.Add(f.Function.Formals[i], f.Args[i].Resolved);
                //}
                //foreach (FrameExpression fe in f.Function.Reads) {
                //    Expression obj = f.Receiver;
                //    if (f.Receiver != null) {
                //        obj = Translator.Substitute(obj, f.Receiver, substMap);
                //    }
                //}

            } else {
                Error(e, "Unexpected expression {0} in region expression resolution.", e.tok.val);

            }
        }

        // Yuyan
        List<Region> GenerateRegionList(Expression e) {
            Contract.Requires(e != null);
            Contract.Ensures(Contract.Result<List<Region>>() != null);
            Contract.Ensures(cce.NonNullElements(Contract.Result<List<Region>>()));
            List<Region> regionList = new List<Region>();

            Type t = e.Type;

            if (UserDefinedType.DenotesClass(t) != null) { // it must be non-null
                UserDefinedType ctype = (UserDefinedType)t;
                ClassDecl cd = (ClassDecl)ctype.ResolvedClass;  // correctness of cast follows from postcondition of DenotesClass
                Contract.Assert(ctype.TypeArgs.Count == cd.TypeArgs.Count);  // follows from the fact that ctype was resolved

                foreach (MemberDecl member in cd.Members) {
                    if (member is Field) {
                        Field f = (Field)member;
                        regionList.Add(new Region(e.Resolved, f));
                    }
                }
            }

            return regionList;
        }

        // Yuyan 
        List<RegionConstructExpression> ResolveFootprintExpression(Expression expr) {
            Contract.Requires(expr != null);
            Contract.Ensures(Contract.Result<List<RegionConstructExpression>>() != null);
            List<RegionConstructExpression> ret = new List<RegionConstructExpression>();
            if (expr is IdentifierSequence) { 
                ret.Add(new RegionConstructExpression(expr.tok, expr));
            } else if (expr is LiteralExpr) {
                // the footprint is emtpy

            } else if (expr is ThisExpr) {
                ret.Add(new RegionConstructExpression(expr.tok, expr));

            } else if (expr is RegionConstructExpression) { // Yuyan: added to resolve RegionConstructExpression
                ret.Add((RegionConstructExpression)expr);
            } else if (expr is RegionFilterExpression) { // Yuyan: added to resolve RegionFiltertExpression
                RegionFilterExpression fte = (RegionFilterExpression)expr;
                ret.Add(new RegionConstructExpression(fte.tok, fte.regionE));
            } else if (expr is FootprintExpression) {
                FootprintExpression fe = (FootprintExpression) expr;
                ret.AddRange(ResolveFootprintExpression(fe.E));                
            } else if (expr is DisplayExpression) {
                DisplayExpression e = (DisplayExpression)expr;
                foreach (Expression ee in e.Elements) {
                    ret.AddRange(ResolveFootprintExpression(ee));
                }
            } else if (expr is MapDisplayExpr) {
                MapDisplayExpr e = (MapDisplayExpr)expr;
                foreach (ExpressionPair p in e.Elements) {
                    ret.AddRange(ResolveFootprintExpression(p.A));
                    ret.AddRange(ResolveFootprintExpression(p.B));
                }
            } else if (expr is ExprDotName) {
                ret.Add(new RegionConstructExpression(expr.tok, expr));

            } else if (expr is FieldSelectExpr) {
                ret.Add(new RegionConstructExpression(expr.tok, expr));
            
            } else if (expr is FunctionCallExpr) {
                ret.Add(new RegionConstructExpression(expr.tok, expr));                            
            } else if (expr is UnaryExpr) {
                UnaryExpr e = (UnaryExpr)expr;
                ret.Add(new RegionConstructExpression(e.E.tok, e.E));
            } else if (expr is BinaryExpr) {
                BinaryExpr e = (BinaryExpr)expr;
                ret.Add(new RegionConstructExpression(e.E0.tok, e.E0));
                ret.Add(new RegionConstructExpression(e.E1.tok, e.E1));

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected expression
            }

            return ret;
        }


        // Yuyan re-define ResolveFrameExpression
        // resolve original frame expression in a region expression way
        // o <==> region{o.*}
        // this <==> region{this.*}
        // o.f <==> region{o.f} if typeof(o.f) != regiontype
        // TODO: collection type
        void ResolveFrameExpression(FrameExpression fe, string kind) {
            Contract.Requires(fe != null);
            Contract.Requires(kind != null);
            ResolveExpression(fe.E, false);
            Type t = fe.E.Type;
            Contract.Assert(t != null);  // follows from postcondition of ResolveExpression

            if (!(t is RegionType)) {
                fe.ResolvedExpression = new RegionConstructExpression(fe.E.tok, fe.E);
                ResolveExpression(fe.ResolvedExpression, false);
            } else {
                fe.ResolvedExpression = fe.E;
                ResolveExpression(fe.ResolvedExpression, false);
            }
        }

        /*
        void ResolveFrameExpression(FrameExpression fe, string kind) {
          Contract.Requires(fe != null);
          Contract.Requires(kind != null);
          ResolveExpression(fe.E, false);
          Type t = fe.E.Type;
          Contract.Assert(t != null);  // follows from postcondition of ResolveExpression
          if (t is CollectionType) {
            t = ((CollectionType)t).Arg;
          }
          if (t is ObjectType) {
            // fine, as long as there's no field name
            if (fe.FieldName != null) {
              Error(fe.E, "type '{0}' does not contain a field named '{1}'", t, fe.FieldName);
            }
          } else if (UserDefinedType.DenotesClass(t) != null) {
            // fine type
            if (fe.FieldName != null) {
              NonProxyType nptype;
              MemberDecl member = ResolveMember(fe.E.tok, t, fe.FieldName, out nptype);
              UserDefinedType ctype = (UserDefinedType)nptype;  // correctness of cast follows from the DenotesClass test above
              if (member == null) {
                // error has already been reported by ResolveMember
              } else if (!(member is Field)) {
                Error(fe.E, "member {0} in type {1} does not refer to a field", fe.FieldName, cce.NonNull(ctype).Name);
              } else {
                Contract.Assert(ctype != null && ctype.ResolvedClass != null);  // follows from postcondition of ResolveMember
                fe.Field = (Field)member;
              }
            }
          } else {
            Error(fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", kind, fe.E.Type);
          }
        }
         */

        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveMethodSignature(Method m) {
            Contract.Requires(m != null);
            scope.PushMarker();
            if (m.SignatureIsOmitted) {
                Error(m, "method signature can be omitted only in refining methods");
            }
            var defaultTypeArguments = m.TypeArgs.Count == 0 ? m.TypeArgs : null;
            // resolve in-parameters
            foreach (Formal p in m.Ins) {
                if (!scope.Push(p.Name, p)) {
                    Error(p, "Duplicate parameter name: {0}", p.Name);
                }
                ResolveType(p.tok, p.Type, defaultTypeArguments, true);
            }
            // resolve out-parameters
            foreach (Formal p in m.Outs) {
                if (!scope.Push(p.Name, p)) {
                    Error(p, "Duplicate parameter name: {0}", p.Name);
                }
                ResolveType(p.tok, p.Type, defaultTypeArguments, true);
            }
            scope.PopMarker();
        }

        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveMethod(Method m) {
            Contract.Requires(m != null);

            // Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
            scope.PushMarker();
            if (m.IsStatic) {
                scope.AllowInstance = false;
            }
            foreach (Formal p in m.Ins) {
                scope.Push(p.Name, p);
            }

            // Start resolving specification...
            foreach (MaybeFreeExpression e in m.Req) {
                ResolveExpression(e.E, false);
                Contract.Assert(e.E.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E.Type, Type.Bool)) {
                    Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type);
                }
            }

            foreach (FrameExpression fe in m.Mod.Expressions) {
                ResolveFrameExpression(fe, "modifies");
            }

            foreach (Expression e in m.Decreases.Expressions) {
                ResolveExpression(e, false);
                // any type is fine
                if (m.IsGhost && e is WildcardExpr) {
                    Error(e, "'decreases *' is not allowed on ghost methods");
                }
            }

            // Add out-parameters to a new scope that will also include the outermost-level locals of the body
            // Don't care about any duplication errors among the out-parameters, since they have already been reported
            scope.PushMarker();
            foreach (Formal p in m.Outs) {
                scope.Push(p.Name, p);
            }

            // attributes are allowed to mention both in- and out-parameters
            ResolveAttributes(m.Attributes, false);

            // ... continue resolving specification
            foreach (MaybeFreeExpression e in m.Ens) {
                ResolveExpression(e.E, true);
                Contract.Assert(e.E.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E.Type, Type.Bool)) {
                    Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
                }
            }

            // Resolve body
            if (m.Body != null) {
                ResolveBlockStatement(m.Body, m.IsGhost, m);
            }

            scope.PopMarker();  // for the out-parameters and outermost-level locals
            scope.PopMarker();  // for the in-parameters
        }

        void ResolveCtorSignature(DatatypeCtor ctor, List<TypeParameter> dtTypeArguments) {
            Contract.Requires(ctor != null);
            Contract.Requires(dtTypeArguments != null);
            foreach (Formal p in ctor.Formals) {
                ResolveType(p.tok, p.Type, dtTypeArguments, false);
            }
        }

        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveIteratorSignature(IteratorDecl iter) {
            Contract.Requires(iter != null);
            scope.PushMarker();
            if (iter.SignatureIsOmitted) {
                Error(iter, "iterator signature can be omitted only in refining methods");
            }
            var defaultTypeArguments = iter.TypeArgs.Count == 0 ? iter.TypeArgs : null;
            // resolve the types of the parameters
            foreach (var p in iter.Ins.Concat(iter.Outs)) {
                ResolveType(p.tok, p.Type, defaultTypeArguments, true);
            }
            // resolve the types of the added fields (in case some of these types would cause the addition of default type arguments)
            foreach (var p in iter.OutsHistoryFields) {
                ResolveType(p.tok, p.Type, defaultTypeArguments, true);
            }
            scope.PopMarker();
        }

        /// <summary>
        /// Assumes type parameters have already been pushed
        /// </summary>
        void ResolveIterator(IteratorDecl iter) {
            Contract.Requires(iter != null);
            Contract.Requires(currentClass == null);
            Contract.Ensures(currentClass == null);

            var initialErrorCount = ErrorCount;

            // Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
            scope.PushMarker();
            scope.AllowInstance = false;  // disallow 'this' from use, which means that the special fields and methods added are not accessible in the syntactically given spec
            iter.Ins.ForEach(p => scope.Push(p.Name, p));

            // Start resolving specification...
            // we start with the decreases clause, because the _decreases<n> fields were only given type proxies before; we'll know
            // the types only after resolving the decreases clause (and it may be that some of resolution has already seen uses of
            // these fields; so, with no further ado, here we go
            Contract.Assert(iter.Decreases.Expressions.Count == iter.DecreasesFields.Count);
            for (int i = 0; i < iter.Decreases.Expressions.Count; i++) {
                var e = iter.Decreases.Expressions[i];
                ResolveExpression(e, false);
                // any type is fine, but associate this type with the corresponding _decreases<n> field
                var d = iter.DecreasesFields[i];
                if (!UnifyTypes(d.Type, e.Type)) {
                    // bummer, there was a use--and a bad use--of the field before, so this won't be the best of error messages
                    Error(e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type);
                }
            }
            foreach (FrameExpression fe in iter.Reads.Expressions) {
                ResolveFrameExpression(fe, "reads");
            }
            foreach (FrameExpression fe in iter.Modifies.Expressions) {
                ResolveFrameExpression(fe, "modifies");
            }
            foreach (MaybeFreeExpression e in iter.Requires) {
                ResolveExpression(e.E, false);
                Contract.Assert(e.E.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E.Type, Type.Bool)) {
                    Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type);
                }
            }

            scope.PopMarker();  // for the in-parameters

            // We resolve the rest of the specification in an instance context.  So mentions of the in- or yield-parameters
            // get resolved as field dereferences (with an implicit "this")
            scope.PushMarker();
            currentClass = iter;
            Contract.Assert(scope.AllowInstance);

            foreach (MaybeFreeExpression e in iter.YieldRequires) {
                ResolveExpression(e.E, false);
                Contract.Assert(e.E.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E.Type, Type.Bool)) {
                    Error(e.E, "Yield precondition must be a boolean (got {0})", e.E.Type);
                }
            }
            foreach (MaybeFreeExpression e in iter.YieldEnsures) {
                ResolveExpression(e.E, true);
                Contract.Assert(e.E.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E.Type, Type.Bool)) {
                    Error(e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type);
                }
            }
            foreach (MaybeFreeExpression e in iter.Ensures) {
                ResolveExpression(e.E, true);
                Contract.Assert(e.E.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E.Type, Type.Bool)) {
                    Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type);
                }
            }

            ResolveAttributes(iter.Attributes, false);

            var postSpecErrorCount = ErrorCount;

            // Resolve body
            if (iter.Body != null) {
                ResolveBlockStatement(iter.Body, false, iter);
            }

            currentClass = null;
            scope.PopMarker();  // pop off the AllowInstance setting

            if (postSpecErrorCount == initialErrorCount) {
                CreateIteratorMethodSpecs(iter);
            }
        }

        /// <summary>
        /// Assumes the specification of the iterator itself has been successfully resolved.
        /// </summary>
        void CreateIteratorMethodSpecs(IteratorDecl iter) {
            Contract.Requires(iter != null);

            // ---------- here comes the constructor ----------
            // same requires clause as the iterator itself
            iter.Member_Init.Req.AddRange(iter.Requires);
            // modifies this;
            iter.Member_Init.Mod.Expressions.Add(new FrameExpression(iter.tok, new ThisExpr(iter.tok), null));
            var ens = iter.Member_Init.Ens;
            foreach (var p in iter.Ins) {
                // ensures this.x == x;
                ens.Add(new MaybeFreeExpression(new BinaryExpr(p.tok, BinaryExpr.Opcode.Eq,
                  new FieldSelectExpr(p.tok, new ThisExpr(p.tok), p.Name), new IdentifierExpr(p.tok, p.Name))));
            }
            foreach (var p in iter.OutsHistoryFields) {
                // ensures this.ys == [];
                ens.Add(new MaybeFreeExpression(new BinaryExpr(p.tok, BinaryExpr.Opcode.Eq,
                  new FieldSelectExpr(p.tok, new ThisExpr(p.tok), p.Name), new SeqDisplayExpr(p.tok, new List<Expression>()))));
            }
            // ensures this.Valid();
            ens.Add(new MaybeFreeExpression(new FunctionCallExpr(iter.tok, "Valid", new ThisExpr(iter.tok), iter.tok, new List<Expression>())));
            // ensures this._reads == old(ReadsClause);
            var modSetSingletons = new List<Expression>();
            Expression frameSet = new SetDisplayExpr(iter.tok, modSetSingletons);
            foreach (var fr in iter.Reads.Expressions) {
                if (fr.FieldName != null) {
                    Error(fr.tok, "sorry, a reads clause for an iterator is not allowed to designate specific fields");
                } else if (fr.E.Type.IsRefType) {
                    modSetSingletons.Add(fr.E);
                } else {
                    frameSet = new BinaryExpr(fr.tok, BinaryExpr.Opcode.Add, frameSet, fr.E);
                }
            }
            ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
              new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_reads"),
              new OldExpr(iter.tok, frameSet))));
            // ensures this._modifies == old(ModifiesClause);
            modSetSingletons = new List<Expression>();
            frameSet = new SetDisplayExpr(iter.tok, modSetSingletons);
            foreach (var fr in iter.Modifies.Expressions) {
                if (fr.FieldName != null) {
                    Error(fr.tok, "sorry, a modifies clause for an iterator is not allowed to designate specific fields");
                } else if (fr.E.Type.IsRefType) {
                    modSetSingletons.Add(fr.E);
                } else {
                    frameSet = new BinaryExpr(fr.tok, BinaryExpr.Opcode.Add, frameSet, fr.E);
                }
            }
            ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
              new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_modifies"),
              new OldExpr(iter.tok, frameSet))));
            // ensures this._new == {};
            ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
              new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"),
              new SetDisplayExpr(iter.tok, new List<Expression>()))));
            // ensures this._decreases0 == old(DecreasesClause[0]) && ...;
            Contract.Assert(iter.Decreases.Expressions.Count == iter.DecreasesFields.Count);
            for (int i = 0; i < iter.Decreases.Expressions.Count; i++) {
                var p = iter.Decreases.Expressions[i];
                ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq,
                  new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), iter.DecreasesFields[i].Name),
                  new OldExpr(iter.tok, p))));
            }

            // ---------- here comes predicate Valid() ----------
            var reads = iter.Member_Valid.Reads;
            reads.Add(new FrameExpression(iter.tok, new ThisExpr(iter.tok), null));  // reads this;
            reads.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_reads"), null));  // reads this._reads;
            reads.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"), null));  // reads this._new;

            // ---------- here comes method MoveNext() ----------
            // requires this.Valid();
            var req = iter.Member_MoveNext.Req;
            req.Add(new MaybeFreeExpression(new FunctionCallExpr(iter.tok, "Valid", new ThisExpr(iter.tok), iter.tok, new List<Expression>())));
            // requires YieldRequires;
            req.AddRange(iter.YieldRequires);
            // modifies this, this._modifies, this._new;
            var mod = iter.Member_MoveNext.Mod.Expressions;
            mod.Add(new FrameExpression(iter.tok, new ThisExpr(iter.tok), null));
            mod.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_modifies"), null));
            mod.Add(new FrameExpression(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"), null));
            // ensures fresh(_new - old(_new));
            ens = iter.Member_MoveNext.Ens;
            ens.Add(new MaybeFreeExpression(new FreshExpr(iter.tok,
              new BinaryExpr(iter.tok, BinaryExpr.Opcode.Sub,
                new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"),
                new OldExpr(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), "_new"))))));
            // ensures more ==> this.Valid();
            ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Imp,
              new IdentifierExpr(iter.tok, "more"),
              new FunctionCallExpr(iter.tok, "Valid", new ThisExpr(iter.tok), iter.tok, new List<Expression>()))));
            // ensures this.ys == if more then old(this.ys) + [this.y] else old(this.ys);
            Contract.Assert(iter.OutsFields.Count == iter.OutsHistoryFields.Count);
            for (int i = 0; i < iter.OutsFields.Count; i++) {
                var y = iter.OutsFields[i];
                var ys = iter.OutsHistoryFields[i];
                var ite = new ITEExpr(iter.tok, new IdentifierExpr(iter.tok, "more"),
                  new BinaryExpr(iter.tok, BinaryExpr.Opcode.Add,
                    new OldExpr(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), ys.Name)),
                    new SeqDisplayExpr(iter.tok, new List<Expression>() { new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), y.Name) })),
                  new OldExpr(iter.tok, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), ys.Name)));
                var eq = new BinaryExpr(iter.tok, BinaryExpr.Opcode.Eq, new FieldSelectExpr(iter.tok, new ThisExpr(iter.tok), ys.Name), ite);
                ens.Add(new MaybeFreeExpression(eq));
            }
            // ensures more ==> YieldEnsures;
            foreach (var ye in iter.YieldEnsures) {
                ens.Add(new MaybeFreeExpression(
                  new BinaryExpr(iter.tok, BinaryExpr.Opcode.Imp, new IdentifierExpr(iter.tok, "more"), ye.E),
                  ye.IsFree));
            }
            // ensures !more ==> Ensures;
            foreach (var e in iter.Ensures) {
                ens.Add(new MaybeFreeExpression(new BinaryExpr(iter.tok, BinaryExpr.Opcode.Imp,
                  new UnaryExpr(iter.tok, UnaryExpr.Opcode.Not, new IdentifierExpr(iter.tok, "more")),
                  e.E),
                  e.IsFree));
            }
            // decreases this._decreases0, this._decreases1, ...;
            Contract.Assert(iter.Decreases.Expressions.Count == iter.DecreasesFields.Count);
            for (int i = 0; i < iter.Decreases.Expressions.Count; i++) {
                var p = iter.Decreases.Expressions[i];
                iter.Member_MoveNext.Decreases.Expressions.Add(new FieldSelectExpr(p.tok, new ThisExpr(p.tok), iter.DecreasesFields[i].Name));
            }
            iter.Member_MoveNext.Decreases.Attributes = iter.Decreases.Attributes;
        }

        /// <summary>
        /// If ResolveType encounters a type "T" that takes type arguments but wasn't given any, then:
        /// * If "defaultTypeArguments" is non-null and "defaultTypeArgument.Count" equals the number
        ///   of type arguments that "T" expects, then use these default type arguments as "T"'s arguments.
        /// * If "allowAutoTypeArguments" is true, then infer "T"'s arguments.
        /// * If "defaultTypeArguments" is non-null AND "allowAutoTypeArguments" is true, then enough
        ///   type parameters will be added to "defaultTypeArguments" to have at least as many type
        ///   parameters as "T" expects, and then a prefix of the "defaultTypeArguments" will be supplied
        ///   as arguments to "T".
        /// </summary>
        public void ResolveType(IToken tok, Type type, List<TypeParameter> defaultTypeArguments, bool allowAutoTypeArguments) {
            Contract.Requires(tok != null);
            Contract.Requires(type != null);
            if (type is BasicType) {
                // nothing to resolve
            } else if (type is MapType) {
                MapType mt = (MapType)type;
                ResolveType(tok, mt.Domain, defaultTypeArguments, allowAutoTypeArguments);
                ResolveType(tok, mt.Range, defaultTypeArguments, allowAutoTypeArguments);
                if (mt.Domain.IsSubrangeType || mt.Range.IsSubrangeType) {
                    Error(tok, "sorry, cannot instantiate collection type with a subrange type");
                }
            } else if (type is CollectionType) {
                var t = (CollectionType)type;
                var argType = t.Arg;
                ResolveType(tok, argType, defaultTypeArguments, allowAutoTypeArguments);
                if (argType.IsSubrangeType) {
                    Error(tok, "sorry, cannot instantiate collection type with a subrange type");
                }
            } else if (type is UserDefinedType) {
                UserDefinedType t = (UserDefinedType)type;
                foreach (Type tt in t.TypeArgs) {
                    ResolveType(t.tok, tt, defaultTypeArguments, allowAutoTypeArguments);
                    if (tt.IsSubrangeType) {
                        Error(t.tok, "sorry, cannot instantiate type parameter with a subrange type");
                    }
                }
                TypeParameter tp = t.Path.Count == 0 ? allTypeParameters.Find(t.Name) : null;
                if (tp != null) {
                    if (t.TypeArgs.Count == 0) {
                        t.ResolvedParam = tp;
                    } else {
                        Error(t.tok, "Type parameter expects no type arguments: {0}", t.Name);
                    }
                } else if (t.ResolvedClass == null) {  // this test is because 'array' is already resolved; TODO: an alternative would be to pre-populate 'classes' with built-in references types like 'array' (and perhaps in the future 'string')
                    TopLevelDecl d = null;

                    int j = 0;
                    var sig = moduleInfo;
                    while (j < t.Path.Count) {
                        if (sig.FindSubmodule(t.Path[j].val, out sig)) {
                            j++;
                            sig = GetSignature(sig);
                        } else {
                            Error(t.Path[j], ModuleNotFoundErrorMessage(j, t.Path));
                            break;
                        }
                    }
                    if (j == t.Path.Count) {
                        if (!sig.TopLevels.TryGetValue(t.Name, out d)) {
                            if (j == 0)
                                Error(t.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name?)", t.Name);
                            else
                                Error(t.tok, "Undeclared type {0} in module {1}", t.Name, t.Path[t.Path.Count - 1].val);
                        }
                    } else {
                        // error has already been reported
                    }

                    if (d == null) {
                        // error has been reported above
                    } else if (d is AmbiguousTopLevelDecl) {
                        Error(t.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", t.Name, ((AmbiguousTopLevelDecl)d).ModuleNames());
                    } else if (d is ArbitraryTypeDecl) {
                        t.ResolvedParam = ((ArbitraryTypeDecl)d).TheType;  // resolve like a type parameter
                    } else {
                        // d is a class or datatype, and it may have type parameters
                        t.ResolvedClass = d;
                        if (d.TypeArgs.Count != t.TypeArgs.Count && t.TypeArgs.Count == 0) {
                            if (allowAutoTypeArguments && defaultTypeArguments == null) {
                                // add type arguments that will be inferred
                                for (int i = 0; i < d.TypeArgs.Count; i++) {
                                    t.TypeArgs.Add(new InferredTypeProxy());
                                }
                            } else if (defaultTypeArguments != null) {
                                // add specific type arguments, drawn from defaultTypeArguments (which may have to be extended)
                                if (allowAutoTypeArguments) {
                                    // add to defaultTypeArguments the necessary number of arguments
                                    for (int i = defaultTypeArguments.Count; i < d.TypeArgs.Count; i++) {
                                        defaultTypeArguments.Add(new TypeParameter(t.tok, "_T" + i));
                                    }
                                }
                                if (allowAutoTypeArguments || d.TypeArgs.Count == defaultTypeArguments.Count) {
                                    Contract.Assert(d.TypeArgs.Count <= defaultTypeArguments.Count);
                                    // automatically supply a prefix of the arguments from defaultTypeArguments
                                    for (int i = 0; i < d.TypeArgs.Count; i++) {
                                        var typeArg = new UserDefinedType(t.tok, defaultTypeArguments[i].Name, new List<Type>(), null);
                                        typeArg.ResolvedParam = defaultTypeArguments[i];  // resolve "typeArg" here
                                        t.TypeArgs.Add(typeArg);
                                    }
                                }
                            }
                        }
                        // defaults and auto have been applied; check if we now have the right number of arguments
                        if (d.TypeArgs.Count != t.TypeArgs.Count) {
                            Error(t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to class/datatype: {2}", t.TypeArgs.Count, d.TypeArgs.Count, t.Name);
                        }
                    }
                }

            } else if (type is TypeProxy) {
                TypeProxy t = (TypeProxy)type;
                if (t.T != null) {
                    ResolveType(tok, t.T, defaultTypeArguments, allowAutoTypeArguments);
                }

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected type
            }
        }

        public bool UnifyTypes(Type a, Type b) {
            Contract.Requires(a != null);
            Contract.Requires(b != null);
            while (a is TypeProxy) {
                TypeProxy proxy = (TypeProxy)a;
                if (proxy.T == null) {
                    // merge a and b; to avoid cycles, first get to the bottom of b
                    while (b is TypeProxy && ((TypeProxy)b).T != null) {
                        b = ((TypeProxy)b).T;
                    }
                    return AssignProxy(proxy, b);
                } else {
                    a = proxy.T;
                }
            }

            while (b is TypeProxy) {
                TypeProxy proxy = (TypeProxy)b;
                if (proxy.T == null) {
                    // merge a and b (we have already got to the bottom of a)
                    return AssignProxy(proxy, a);
                } else {
                    b = proxy.T;
                }
            }

#if !NO_CHEAP_OBJECT_WORKAROUND
            if (a is ObjectType || b is ObjectType) {  // TODO: remove this temporary hack
                var other = a is ObjectType ? b : a;
                if (other is BoolType || other is IntType || other is SetType || other is SeqType || other is RegionType /* Yuyan: not know why*/ || other.IsDatatype) {
                    return false;
                }
                // allow anything else with object; this is BOGUS
                return true;
            }
#endif
            // Now, a and b are non-proxies and stand for the same things as the original a and b, respectively.

            if (a is BoolType) {
                return b is BoolType;
            } else if (a is IntType) {
                return b is IntType;
            } else if (a is RegionType) { // Yuyan: type check for regiontype
                return b is RegionType;
            } else if (a is ObjectType) {
                return b is ObjectType;
            } else if (a is SetType) {
                return b is SetType && UnifyTypes(((SetType)a).Arg, ((SetType)b).Arg);
            } else if (a is MultiSetType) {
                return b is MultiSetType && UnifyTypes(((MultiSetType)a).Arg, ((MultiSetType)b).Arg);
            } else if (a is MapType) {
                return b is MapType && UnifyTypes(((MapType)a).Domain, ((MapType)b).Domain) && UnifyTypes(((MapType)a).Range, ((MapType)b).Range);
            } else if (a is SeqType) {
                return b is SeqType && UnifyTypes(((SeqType)a).Arg, ((SeqType)b).Arg);
            } else if (a is UserDefinedType) {
                if (!(b is UserDefinedType)) {
                    return false;
                }
                UserDefinedType aa = (UserDefinedType)a;
                UserDefinedType bb = (UserDefinedType)b;
                if (aa.ResolvedClass != null && aa.ResolvedClass == bb.ResolvedClass) {
                    // these are both resolved class/datatype types
                    Contract.Assert(aa.TypeArgs.Count == bb.TypeArgs.Count);
                    bool successSoFar = true;
                    for (int i = 0; i < aa.TypeArgs.Count; i++) {
                        if (!UnifyTypes(aa.TypeArgs[i], bb.TypeArgs[i])) {
                            successSoFar = false;
                        }
                    }
                    return successSoFar;
                } else if (aa.ResolvedParam != null && aa.ResolvedParam == bb.ResolvedParam) {
                    // these are both resolved type parameters
                    Contract.Assert(aa.TypeArgs.Count == 0 && bb.TypeArgs.Count == 0);
                    return true;
                } else {
                    // something is wrong; either aa or bb wasn't properly resolved, or they don't unify
                    return false;
                }

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected type
            }
        }

        bool AssignProxy(TypeProxy proxy, Type t) {
            Contract.Requires(proxy != null);
            Contract.Requires(t != null);
            Contract.Requires(proxy.T == null);
            Contract.Requires(!(t is TypeProxy) || ((TypeProxy)t).T == null);
            //modifies proxy.T, ((TypeProxy)t).T;  // might also change t.T if t is a proxy
            Contract.Ensures(Contract.Result<bool>() == (proxy == t || proxy.T != null || (t is TypeProxy && ((TypeProxy)t).T != null)));
            if (proxy == t) {
                // they are already in the same equivalence class
                return true;

            } else if (proxy is UnrestrictedTypeProxy) {
                // it's fine to redirect proxy to t (done below)

            } else if (t is UnrestrictedTypeProxy) {
                // merge proxy and t by redirecting t to proxy, rather than the other way around
                ((TypeProxy)t).T = proxy;
                return true;

            } else if (t is RestrictedTypeProxy) {
                // Both proxy and t are restricted type proxies.  To simplify unification, order proxy and t
                // according to their types.
                RestrictedTypeProxy r0 = (RestrictedTypeProxy)proxy;
                RestrictedTypeProxy r1 = (RestrictedTypeProxy)t;
                if (r0.OrderID <= r1.OrderID) {
                    return AssignRestrictedProxies(r0, r1);
                } else {
                    return AssignRestrictedProxies(r1, r0);
                }

                // In the remaining cases, proxy is a restricted proxy and t is a non-proxy
            } else if (proxy is DatatypeProxy) {
                if (t.IsIndDatatype) {
                    // all is fine, proxy can be redirected to t
                } else {
                    return false;
                }

            } else if (proxy is ObjectTypeProxy) {
                if (t is ObjectType || UserDefinedType.DenotesClass(t) != null) {
                    // all is fine, proxy can be redirected to t
                } else {
                    return false;
                }

            } else if (proxy is CollectionTypeProxy) {
                CollectionTypeProxy collProxy = (CollectionTypeProxy)proxy;
                if (t is CollectionType) {
                    if (!UnifyTypes(collProxy.Arg, ((CollectionType)t).Arg)) {
                        return false;
                    }
                } else {
                    return false;
                }

            } else if (proxy is OperationTypeProxy) {
                OperationTypeProxy opProxy = (OperationTypeProxy)proxy;
                if (t is IntType || t is SetType || t is MultiSetType || (opProxy.AllowSeq && t is SeqType) || t is RegionType /* Yuyan added*/) {
                    // this is the expected case
                } else {
                    return false;
                }

            } else if (proxy is IndexableTypeProxy) {
                IndexableTypeProxy iProxy = (IndexableTypeProxy)proxy;
                if (t is SeqType) {
                    if (!UnifyTypes(iProxy.Arg, ((SeqType)t).Arg)) {
                        return false;
                    }
                    if (!UnifyTypes(iProxy.Domain, Type.Int)) {
                        return false;
                    }
                } else if (t.IsArrayType && (t.AsArrayType).Dims == 1) {
                    Type elType = UserDefinedType.ArrayElementType(t);
                    if (!UnifyTypes(iProxy.Arg, elType)) {
                        return false;
                    }
                    if (!UnifyTypes(iProxy.Domain, Type.Int)) {
                        return false;
                    }
                } else if (t is MapType) {
                    if (!UnifyTypes(iProxy.Arg, ((MapType)t).Range)) {
                        return false;
                    }
                    if (!UnifyTypes(iProxy.Domain, ((MapType)t).Domain)) {
                        return false;
                    }
                } else {
                    return false;
                }

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected proxy type
            }

            // do the merge, but never infer a subrange type
            if (t is NatType) {
                proxy.T = Type.Int;
            } else {
                proxy.T = t;
            }
            return true;
        }

        bool AssignRestrictedProxies(RestrictedTypeProxy a, RestrictedTypeProxy b) {
            Contract.Requires(a != null);
            Contract.Requires(b != null);
            Contract.Requires(a != b);
            Contract.Requires(a.T == null && b.T == null);
            Contract.Requires(a.OrderID <= b.OrderID);
            //modifies a.T, b.T;
            Contract.Ensures(!Contract.Result<bool>() || a.T != null || b.T != null);

            if (a is DatatypeProxy) {
                if (b is DatatypeProxy) {
                    // all is fine
                    a.T = b;
                    return true;
                } else {
                    return false;
                }
            } else if (a is ObjectTypeProxy) {
                if (b is ObjectTypeProxy) {
                    // all is fine
                    a.T = b;
                    return true;
                } else if (b is IndexableTypeProxy) {
                    // the intersection of ObjectTypeProxy and IndexableTypeProxy is an array type
                    a.T = builtIns.ArrayType(1, ((IndexableTypeProxy)b).Arg);
                    b.T = a.T;
                    return true;
                } else {
                    return false;
                }

            } else if (a is CollectionTypeProxy) {
                if (b is CollectionTypeProxy) {
                    a.T = b;
                    return UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg);
                } else if (b is OperationTypeProxy) {
                    if (((OperationTypeProxy)b).AllowSeq) {
                        b.T = a;  // a is a stronger constraint than b
                    } else {
                        // a says set<T>,seq<T> and b says int,set; the intersection is set<T>
                        a.T = new SetType(((CollectionTypeProxy)a).Arg);
                        b.T = a.T;
                    }
                    return true;
                } else if (b is IndexableTypeProxy) {
                    CollectionTypeProxy pa = (CollectionTypeProxy)a;
                    IndexableTypeProxy pb = (IndexableTypeProxy)b;
                    // a and b could be a map or a sequence
                    return true;
                } else {
                    Contract.Assert(false); throw new cce.UnreachableException();  // unexpected restricted-proxy type
                }

            } else if (a is OperationTypeProxy) {
                OperationTypeProxy pa = (OperationTypeProxy)a;
                if (b is OperationTypeProxy) {
                    if (!pa.AllowSeq || ((OperationTypeProxy)b).AllowSeq) {
                        b.T = a;
                    } else {
                        a.T = b;  // b has the stronger requirement
                    }
                    return true;
                } else {
                    IndexableTypeProxy pb = (IndexableTypeProxy)b;  // cast justification:  lse we have unexpected restricted-proxy type
                    if (pa.AllowSeq) {
                        // strengthen a and b to a sequence type
                        b.T = new SeqType(pb.Arg);
                        a.T = b.T;
                        return true;
                    } else {
                        return false;
                    }
                }

            } else if (a is IndexableTypeProxy) {
                Contract.Assert(b is IndexableTypeProxy);  // else we have unexpected restricted-proxy type
                a.T = b;
                return UnifyTypes(((IndexableTypeProxy)a).Arg, ((IndexableTypeProxy)b).Arg);

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected restricted-proxy type
            }
        }

        /// <summary>
        /// "specContextOnly" means that the statement must be erasable, that is, it should be okay to omit it
        /// at run time.  That means it must not have any side effects on non-ghost variables, for example.
        /// </summary>
        public void ResolveStatement(Statement stmt, bool specContextOnly, ICodeContext codeContext) {
            Contract.Requires(stmt != null);
            Contract.Requires(codeContext != null);
            if (stmt is PredicateStmt) {
                PredicateStmt s = (PredicateStmt)stmt;
                ResolveAttributes(s.Attributes, false);
                s.IsGhost = true;
                ResolveExpression(s.Expr, true);
                Contract.Assert(s.Expr.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(s.Expr.Type, Type.Bool)) {
                    Error(s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type);
                }

            } else if (stmt is PrintStmt) {
                PrintStmt s = (PrintStmt)stmt;
                ResolveAttributeArgs(s.Args, false, false);
                if (specContextOnly) {
                    Error(stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
                }

            } else if (stmt is BreakStmt) {
                var s = (BreakStmt)stmt;
                if (s.TargetLabel != null) {
                    Statement target = labeledStatements.Find(s.TargetLabel);
                    if (target == null) {
                        Error(s, "break label is undefined or not in scope: {0}", s.TargetLabel);
                    } else {
                        s.TargetStmt = target;
                        bool targetIsLoop = target is WhileStmt || target is AlternativeLoopStmt;
                        if (specContextOnly && !s.TargetStmt.IsGhost && !inSpecOnlyContext[s.TargetStmt]) {
                            Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure"));
                        }
                    }
                } else {
                    if (loopStack.Count < s.BreakCount) {
                        Error(s, "trying to break out of more loop levels than there are enclosing loops");
                    } else {
                        Statement target = loopStack[loopStack.Count - s.BreakCount];
                        if (target.Labels == null) {
                            // make sure there is a label, because the compiler and translator will want to see a unique ID
                            target.Labels = new LList<Label>(new Label(target.Tok, null), null);
                        }
                        s.TargetStmt = target;
                        if (specContextOnly && !target.IsGhost && !inSpecOnlyContext[target]) {
                            Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost loop");
                        }
                    }
                }

            } else if (stmt is ProduceStmt) {
                var kind = stmt is YieldStmt ? "yield" : "return";
                if (stmt is YieldStmt && !(codeContext is IteratorDecl)) {
                    Error(stmt, "yield statement is allowed only in iterators");
                } else if (stmt is ReturnStmt && !(codeContext is Method)) {
                    Error(stmt, "return statement is allowed only in method");
                } else if (specContextOnly && !codeContext.IsGhost) {
                    Error(stmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind);
                }
                var s = (ProduceStmt)stmt;
                if (s.rhss != null) {
                    if (codeContext.Outs.Count != s.rhss.Count)
                        Error(s, "number of {2} parameters does not match declaration (found {0}, expected {1})", s.rhss.Count, codeContext.Outs.Count, kind);
                    else {
                        Contract.Assert(s.rhss.Count > 0);
                        // Create a hidden update statement using the out-parameter formals, resolve the RHS, and check that the RHS is good.
                        List<Expression> formals = new List<Expression>();
                        int i = 0;
                        foreach (Formal f in codeContext.Outs) {
                            IdentifierExpr ident = new IdentifierExpr(f.tok, f.Name);
                            ident.Var = f;
                            ident.Type = ident.Var.Type;
                            Contract.Assert(f.Type != null);
                            formals.Add(ident);
                            // link the receiver parameter properly:
                            if (s.rhss[i] is TypeRhs) {
                                var r = (TypeRhs)s.rhss[i];
                                if (r.InitCall != null) {
                                    r.InitCall.Receiver = ident;
                                }
                            }
                            i++;
                        }
                        s.hiddenUpdate = new UpdateStmt(s.Tok, formals, s.rhss, true);
                        // resolving the update statement will check for return/yield statement specifics.
                        ResolveStatement(s.hiddenUpdate, specContextOnly, codeContext);
                    }
                } else {// this is a regular return/yield statement.
                    s.hiddenUpdate = null;
                }
            } else if (stmt is ConcreteUpdateStatement) {
                ResolveUpdateStmt((ConcreteUpdateStatement)stmt, specContextOnly, codeContext);
            } else if (stmt is VarDeclStmt) {
                var s = (VarDeclStmt)stmt;
                foreach (var vd in s.Lhss) {
                    ResolveStatement(vd, specContextOnly, codeContext);
                    s.ResolvedStatements.Add(vd);
                }
                if (s.Update != null) {
                    ResolveStatement(s.Update, specContextOnly, codeContext);
                    s.ResolvedStatements.Add(s.Update);
                }

            } else if (stmt is AssignStmt) {
                AssignStmt s = (AssignStmt)stmt;
                int prevErrorCount = ErrorCount;
                if (s.Lhs is SeqSelectExpr) {
                    ResolveSeqSelectExpr((SeqSelectExpr)s.Lhs, true, false);  // allow ghosts for now, tighted up below
                } else {
                    ResolveExpression(s.Lhs, true);  // allow ghosts for now, tighted up below
                }
                bool lhsResolvedSuccessfully = ErrorCount == prevErrorCount;
                Contract.Assert(s.Lhs.Type != null);  // follows from postcondition of ResolveExpression
                // check that LHS denotes a mutable variable or a field
                bool lvalueIsGhost = false;
                var lhs = s.Lhs.Resolved;
                if (lhs is IdentifierExpr) {
                    IVariable var = ((IdentifierExpr)lhs).Var;
                    if (var == null) {
                        // the LHS didn't resolve correctly; some error would already have been reported
                    } else {
                        lvalueIsGhost = var.IsGhost || codeContext.IsGhost;
                        if (!var.IsMutable) {
                            Error(stmt, "LHS of assignment must denote a mutable variable or field");
                        }
                        if (!lvalueIsGhost && specContextOnly) {
                            Error(stmt, "Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
                        }
                    }
                } else if (lhs is FieldSelectExpr) {
                    var fse = (FieldSelectExpr)lhs;
                    if (fse.Field != null) {  // otherwise, an error was reported above
                        lvalueIsGhost = fse.Field.IsGhost;
                        if (!lvalueIsGhost) {
                            if (specContextOnly) {
                                Error(stmt, "Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
                            } else {
                                // It is now that we wish we would have resolved s.Lhs to not allow ghosts.  Too late, so we do
                                // the next best thing.
                                if (lhsResolvedSuccessfully && UsesSpecFeatures(fse.Obj)) {
                                    Error(stmt, "Assignment to non-ghost field is not allowed to use specification-only expressions in the receiver");
                                }
                            }
                        }
                        if (!fse.Field.IsUserMutable) {
                            Error(stmt, "LHS of assignment does not denote a mutable field");
                        }
                    }
                } else if (lhs is SeqSelectExpr) {
                    var slhs = (SeqSelectExpr)lhs;
                    // LHS is fine, provided the "sequence" is really an array
                    if (lhsResolvedSuccessfully) {
                        Contract.Assert(slhs.Seq.Type != null);
                        if (!UnifyTypes(slhs.Seq.Type, builtIns.ArrayType(1, new InferredTypeProxy()))) {
                            Error(slhs.Seq, "LHS of array assignment must denote an array element (found {0})", slhs.Seq.Type);
                        }
                        if (specContextOnly) {
                            Error(stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
                        }
                        if (!slhs.SelectOne) {
                            Error(stmt, "cannot assign to a range of array elements (try the 'parallel' statement)");
                        }
                    }

                } else if (lhs is MultiSelectExpr) {
                    if (specContextOnly) {
                        Error(stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)");
                    }

                } else {
                    Error(stmt, "LHS of assignment must denote a mutable variable or field");
                }

                s.IsGhost = lvalueIsGhost;
                Type lhsType = s.Lhs.Type;
                if (lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne) {
                    Error(stmt, "cannot assign to a range of array elements (try the 'parallel' statement)");
                    //lhsType = UserDefinedType.ArrayElementType(lhsType);
                } else {
                    if (s.Rhs is ExprRhs) {
                        ExprRhs rr = (ExprRhs)s.Rhs;
                        ResolveExpression(rr.Expr, true);
                        if (!lvalueIsGhost) {
                            CheckIsNonGhost(rr.Expr);
                        }
                        Contract.Assert(rr.Expr.Type != null);  // follows from postcondition of ResolveExpression
                        if (!UnifyTypes(lhsType, rr.Expr.Type)) {
                            Error(stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType);
                        }
                    } else if (s.Rhs is TypeRhs) {
                        TypeRhs rr = (TypeRhs)s.Rhs;
                        Type t = ResolveTypeRhs(rr, stmt, lvalueIsGhost, codeContext);
                        if (!lvalueIsGhost) {
                            if (rr.ArrayDimensions != null) {
                                foreach (var dim in rr.ArrayDimensions) {
                                    CheckIsNonGhost(dim);
                                }
                            }
                            if (rr.InitCall != null) {
                                foreach (var arg in rr.InitCall.Args) {
                                    CheckIsNonGhost(arg);
                                }
                            }
                        }
                        if (!UnifyTypes(lhsType, t)) {
                            Error(stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType);
                        }
                    } else if (s.Rhs is HavocRhs) {
                        // nothing else to do
                    } else {
                        Contract.Assert(false); throw new cce.UnreachableException();  // unexpected RHS
                    }
                }
            } else if (stmt is VarDecl) {
                VarDecl s = (VarDecl)stmt;
                if (s.OptionalType != null) {
                    ResolveType(stmt.Tok, s.OptionalType, null, true);
                    s.type = s.OptionalType;
                }
                // now that the declaration has been processed, add the name to the scope
                if (!scope.Push(s.Name, s)) {
                    Error(s, "Duplicate local-variable name: {0}", s.Name);
                }
                if (specContextOnly) {
                    // a local variable in a specification-only context might as well be ghost
                    s.IsGhost = true;
                }

            } else if (stmt is CallStmt) {
                CallStmt s = (CallStmt)stmt;
                ResolveCallStmt(s, specContextOnly, codeContext, null);

            } else if (stmt is BlockStmt) {
                scope.PushMarker();
                ResolveBlockStatement((BlockStmt)stmt, specContextOnly, codeContext);
                scope.PopMarker();

            } else if (stmt is IfStmt) {
                IfStmt s = (IfStmt)stmt;
                bool branchesAreSpecOnly = specContextOnly;
                if (s.Guard != null) {
                    int prevErrorCount = ErrorCount;
                    ResolveExpression(s.Guard, true);
                    Contract.Assert(s.Guard.Type != null);  // follows from postcondition of ResolveExpression
                    bool successfullyResolved = ErrorCount == prevErrorCount;
                    if (!UnifyTypes(s.Guard.Type, Type.Bool)) {
                        Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
                    }
                    if (!specContextOnly && successfullyResolved) {
                        branchesAreSpecOnly = UsesSpecFeatures(s.Guard);
                    }
                }
                s.IsGhost = branchesAreSpecOnly;
                ResolveStatement(s.Thn, branchesAreSpecOnly, codeContext);
                if (s.Els != null) {
                    ResolveStatement(s.Els, branchesAreSpecOnly, codeContext);
                }

            } else if (stmt is AlternativeStmt) {
                var s = (AlternativeStmt)stmt;
                s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, null, codeContext);

            } else if (stmt is WhileStmt) {
                WhileStmt s = (WhileStmt)stmt;
                bool bodyMustBeSpecOnly = specContextOnly;
                if (s.Guard != null) {
                    int prevErrorCount = ErrorCount;
                    ResolveExpression(s.Guard, true);
                    Contract.Assert(s.Guard.Type != null);  // follows from postcondition of ResolveExpression
                    bool successfullyResolved = ErrorCount == prevErrorCount;
                    if (!UnifyTypes(s.Guard.Type, Type.Bool)) {
                        Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type);
                    }
                    if (!specContextOnly && successfullyResolved) {
                        bodyMustBeSpecOnly = UsesSpecFeatures(s.Guard);
                    }
                }

                foreach (MaybeFreeExpression inv in s.Invariants) {
                    ResolveExpression(inv.E, true);
                    Contract.Assert(inv.E.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(inv.E.Type, Type.Bool)) {
                        Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type);
                    }
                }

                foreach (Expression e in s.Decreases.Expressions) {
                    ResolveExpression(e, true);
                    if (bodyMustBeSpecOnly && e is WildcardExpr) {
                        Error(e, "'decreases *' is not allowed on ghost loops");
                    }
                    // any type is fine
                }

                if (s.Mod.Expressions != null) {
                    foreach (FrameExpression fe in s.Mod.Expressions) {
                        ResolveFrameExpression(fe, "modifies");
                    }
                }
                s.IsGhost = bodyMustBeSpecOnly;
                loopStack.Add(s);  // push
                if (s.Labels == null) {  // otherwise, "s" is already in "inSpecOnlyContext" map
                    inSpecOnlyContext.Add(s, specContextOnly);
                }

                ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext);
                loopStack.RemoveAt(loopStack.Count - 1);  // pop

            } else if (stmt is AlternativeLoopStmt) {
                var s = (AlternativeLoopStmt)stmt;
                s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, s, codeContext);
                foreach (MaybeFreeExpression inv in s.Invariants) {
                    ResolveExpression(inv.E, true);
                    Contract.Assert(inv.E.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(inv.E.Type, Type.Bool)) {
                        Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type);
                    }
                }

                foreach (Expression e in s.Decreases.Expressions) {
                    ResolveExpression(e, true);
                    if (s.IsGhost && e is WildcardExpr) {
                        Error(e, "'decreases *' is not allowed on ghost loops");
                    }
                    // any type is fine
                }

            } else if (stmt is ParallelStmt) {
                var s = (ParallelStmt)stmt;

                int prevErrorCount = ErrorCount;
                scope.PushMarker();
                foreach (BoundVar v in s.BoundVars) {
                    if (!scope.Push(v.Name, v)) {
                        Error(v, "Duplicate bound-variable name: {0}", v.Name);
                    }
                    ResolveType(v.tok, v.Type, null, true);
                }
                ResolveExpression(s.Range, true);
                Contract.Assert(s.Range.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(s.Range.Type, Type.Bool)) {
                    Error(stmt, "range restriction in parallel statement must be of type bool (instead got {0})", s.Range.Type);
                }
                foreach (var ens in s.Ens) {
                    ResolveExpression(ens.E, true);
                    Contract.Assert(ens.E.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(ens.E.Type, Type.Bool)) {
                        Error(ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type);
                    }
                }
                // Since the range and postconditions are more likely to infer the types of the bound variables, resolve them
                // first (above) and only then resolve the attributes (below).
                ResolveAttributes(s.Attributes, true);

                bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == ErrorCount && UsesSpecFeatures(s.Range));
                if (!bodyMustBeSpecOnly && prevErrorCount == ErrorCount) {
                    var missingBounds = new List<BoundVar>();
                    s.Bounds = DiscoverBounds(s.Tok, s.BoundVars, s.Range, true, false, missingBounds);
                    if (missingBounds.Count != 0) {
                        bodyMustBeSpecOnly = true;
                    }
                }
                s.IsGhost = bodyMustBeSpecOnly;

                // clear the labels for the duration of checking the body, because break statements are not allowed to leave a parallel statement
                var prevLblStmts = labeledStatements;
                var prevLoopStack = loopStack;
                labeledStatements = new Scope<Statement>();
                loopStack = new List<Statement>();
                ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext);
                labeledStatements = prevLblStmts;
                loopStack = prevLoopStack;
                scope.PopMarker();

                if (prevErrorCount == ErrorCount) {
                    // determine the Kind and run some additional checks on the body
                    if (s.Ens.Count != 0) {
                        // The only supported kind with ensures clauses is Proof.
                        s.Kind = ParallelStmt.ParBodyKind.Proof;
                    } else {
                        // There are two special cases:
                        // * Assign, which is the only kind of the parallel statement that allows a heap update.
                        // * Call, which is a single call statement with no side effects or output parameters.
                        // The effect of Assign and the postcondition of Call will be seen outside the parallel
                        // statement.
                        Statement s0 = s.S0;
                        if (s0 is AssignStmt) {
                            s.Kind = ParallelStmt.ParBodyKind.Assign;
                        } else if (s0 is CallStmt) {
                            s.Kind = ParallelStmt.ParBodyKind.Call;
                        } else {
                            s.Kind = ParallelStmt.ParBodyKind.Proof;
                            if (s.Body is BlockStmt && ((BlockStmt)s.Body).Body.Count == 0) {
                                // an empty statement, so don't produce any warning
                            } else {
                                Warning(s.Tok, "the conclusion of the body of this parallel statement will not be known outside the parallel statement; consider using an 'ensures' clause");
                            }
                        }
                    }
                    CheckParallelBodyRestrictions(s.Body, s.Kind);
                }

            } else if (stmt is CalcStmt) {
                var prevErrorCount = ErrorCount;
                CalcStmt s = (CalcStmt)stmt;
                s.IsGhost = true;
                if (s.Lines.Count > 0) {
                    var resOp = s.Op;
                    var e0 = s.Lines.First();
                    ResolveExpression(e0, true);
                    Contract.Assert(e0.Type != null);  // follows from postcondition of ResolveExpression
                    for (int i = 1; i < s.Lines.Count; i++) {
                        var e1 = s.Lines[i];
                        ResolveExpression(e1, true);
                        Contract.Assert(e1.Type != null);  // follows from postcondition of ResolveExpression
                        if (!UnifyTypes(e0.Type, e1.Type)) {
                            Error(e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type);
                        } else {
                            BinaryExpr step;
                            var op = s.CustomOps[i - 1];
                            if (op == null) {
                                step = new BinaryExpr(e0.tok, s.Op, e0, e1); // Use calc-wide operator
                            } else {
                                step = new BinaryExpr(e0.tok, (BinaryExpr.Opcode)op, e0, e1); // Use custom line operator
                                Contract.Assert(CalcStmt.ResultOp(resOp, (BinaryExpr.Opcode)op) != null); // This was checked during parsing
                                resOp = (BinaryExpr.Opcode)CalcStmt.ResultOp(resOp, (BinaryExpr.Opcode)op);
                            }
                            ResolveExpression(step, true);
                            s.Steps.Add(step);
                        }
                        e0 = e1;
                    }
                    foreach (var h in s.Hints) {
                        ResolveStatement(h, true, codeContext);
                    }
                    if (prevErrorCount == ErrorCount && s.Steps.Count > 0) {
                        // do not build Result if there were errors, as it might be ill-typed and produce unnecessary resolution errors
                        s.Result = new BinaryExpr(s.Tok, resOp, s.Lines.First(), s.Lines.Last());
                        ResolveExpression(s.Result, true);
                    }
                }
                Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == s.Hints.Count);
                Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == 0 || s.Result != null);

            } else if (stmt is MatchStmt) {
                MatchStmt s = (MatchStmt)stmt;
                bool bodyIsSpecOnly = specContextOnly;
                int prevErrorCount = ErrorCount;
                ResolveExpression(s.Source, true);
                Contract.Assert(s.Source.Type != null);  // follows from postcondition of ResolveExpression
                bool successfullyResolved = ErrorCount == prevErrorCount;
                if (!specContextOnly && successfullyResolved) {
                    bodyIsSpecOnly = UsesSpecFeatures(s.Source);
                }
                UserDefinedType sourceType = null;
                DatatypeDecl dtd = null;
                Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
                if (s.Source.Type.IsDatatype) {
                    sourceType = (UserDefinedType)s.Source.Type;
                    dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
                }
                Dictionary<string, DatatypeCtor> ctors;
                if (dtd == null) {
                    Error(s.Source, "the type of the match source expression must be a datatype (instead found {0})", s.Source.Type);
                    ctors = null;
                } else {
                    Contract.Assert(sourceType != null);  // dtd and sourceType are set together above
                    ctors = datatypeCtors[dtd];
                    Contract.Assert(ctors != null);  // dtd should have been inserted into datatypeCtors during a previous resolution stage

                    // build the type-parameter substitution map for this use of the datatype
                    for (int i = 0; i < dtd.TypeArgs.Count; i++) {
                        subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]);
                    }
                }
                s.IsGhost = bodyIsSpecOnly;

                Dictionary<string, object> memberNamesUsed = new Dictionary<string, object>();  // this is really a set
                foreach (MatchCaseStmt mc in s.Cases) {
                    DatatypeCtor ctor = null;
                    if (ctors != null) {
                        Contract.Assert(dtd != null);
                        if (!ctors.TryGetValue(mc.Id, out ctor)) {
                            Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name);
                        } else {
                            Contract.Assert(ctor != null);  // follows from postcondition of TryGetValue
                            mc.Ctor = ctor;
                            if (ctor.Formals.Count != mc.Arguments.Count) {
                                Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count);
                            }
                            if (memberNamesUsed.ContainsKey(mc.Id)) {
                                Error(mc.tok, "member {0} appears in more than one case", mc.Id);
                            } else {
                                memberNamesUsed.Add(mc.Id, null);  // add mc.Id to the set of names used
                            }
                        }
                    }
                    scope.PushMarker();
                    int i = 0;
                    foreach (BoundVar v in mc.Arguments) {
                        if (!scope.Push(v.Name, v)) {
                            Error(v, "Duplicate parameter name: {0}", v.Name);
                        }
                        ResolveType(v.tok, v.Type, null, true);
                        if (ctor != null && i < ctor.Formals.Count) {
                            Formal formal = ctor.Formals[i];
                            Type st = SubstType(formal.Type, subst);
                            if (!UnifyTypes(v.Type, st)) {
                                Error(stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st);
                            }
                            v.IsGhost = formal.IsGhost;
                        }
                        i++;
                    }
                    foreach (Statement ss in mc.Body) {
                        ResolveStatement(ss, bodyIsSpecOnly, codeContext);
                    }
                    scope.PopMarker();
                }
                if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
                    // We could complain about the syntactic omission of constructors:
                    //   Error(stmt, "match statement does not cover all constructors");
                    // but instead we let the verifier do a semantic check.
                    // So, for now, record the missing constructors:
                    foreach (var ctr in dtd.Ctors) {
                        if (!memberNamesUsed.ContainsKey(ctr.Name)) {
                            s.MissingCases.Add(ctr);
                        }
                    }
                    Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count);
                }


            } else if (stmt is SkeletonStatement) {
                var s = (SkeletonStatement)stmt;
                Error(s.Tok, "skeleton statements are allowed only in refining methods");
                // nevertheless, resolve the underlying statement; hey, why not
                if (s.S != null) {
                    ResolveStatement(s.S, specContextOnly, codeContext);
                }
            } else {
                Contract.Assert(false); throw new cce.UnreachableException();
            }
        }
        private void ResolveUpdateStmt(ConcreteUpdateStatement s, bool specContextOnly, ICodeContext codeContext) {
            Contract.Requires(codeContext != null);

            int prevErrorCount = ErrorCount;
            // First, resolve all LHS's and expression-looking RHS's.
            SeqSelectExpr arrayRangeLhs = null;
            var update = s as UpdateStmt;

            foreach (var lhs in s.Lhss) {
                var ec = ErrorCount;
                if (lhs is SeqSelectExpr) {
                    var sse = (SeqSelectExpr)lhs;
                    ResolveSeqSelectExpr(sse, true, true);
                    if (arrayRangeLhs == null && !sse.SelectOne) {
                        arrayRangeLhs = sse;
                    }
                } else {
                    ResolveExpression(lhs, true);
                }
                if (update == null && ec == ErrorCount && specContextOnly && !AssignStmt.LhsIsToGhost(lhs)) {
                    Error(lhs, "cannot assign to non-ghost variable in a ghost context");
                }
            }
            IToken firstEffectfulRhs = null;
            CallRhs callRhs = null;
            // Resolve RHSs
            if (update == null) {
                var suchThat = (AssignSuchThatStmt)s;  // this is the other possible subclass
                ResolveExpression(suchThat.Expr, true);
                if (suchThat.AssumeToken == null) {
                    // to ease in the verification, only allow local variables as LHSs
                    var lhsNames = new Dictionary<string, object>();
                    foreach (var lhs in s.Lhss) {
                        if (!(lhs.Resolved is IdentifierExpr)) {
                            Error(lhs, "the assign-such-that statement currently only supports local-variable LHSs");
                        } else {
                            var ie = (IdentifierExpr)lhs.Resolved;
                            if (lhsNames.ContainsKey(ie.Name)) {
                                // disallow same LHS.
                                Error(s, "duplicate variable in left-hand side of assign-such-that statement: {0}", ie.Name);
                            } else {
                                lhsNames.Add(ie.Name, null);
                            }
                        }
                    }
                }
            } else {
                foreach (var rhs in update.Rhss) {
                    bool isEffectful;
                    if (rhs is TypeRhs) {
                        var tr = (TypeRhs)rhs;
                        ResolveTypeRhs(tr, s, specContextOnly, codeContext);
                        isEffectful = tr.InitCall != null;
                    } else if (rhs is HavocRhs) {
                        isEffectful = false;
                    } else {
                        var er = (ExprRhs)rhs;
                        if (er.Expr is IdentifierSequence) {
                            var cRhs = ResolveIdentifierSequence((IdentifierSequence)er.Expr, true, true);
                            isEffectful = cRhs != null;
                            callRhs = callRhs ?? cRhs;
                        } else if (er.Expr is FunctionCallExpr) {
                            var cRhs = ResolveFunctionCallExpr((FunctionCallExpr)er.Expr, true, true);
                            isEffectful = cRhs != null;
                            callRhs = callRhs ?? cRhs;
                        } else {
                            ResolveExpression(er.Expr, true);
                            isEffectful = false;
                        }
                    }
                    if (isEffectful && firstEffectfulRhs == null) {
                        firstEffectfulRhs = rhs.Tok;
                    }
                }
            }
            // check for duplicate identifiers on the left (full duplication checking for references and the like is done during verification)
            var lhsNameSet = new Dictionary<string, object>();
            foreach (var lhs in s.Lhss) {
                var ie = lhs.Resolved as IdentifierExpr;
                if (ie != null) {
                    if (lhsNameSet.ContainsKey(ie.Name)) {
                        if (callRhs != null)
                            // only allow same LHS in a multiassignment, not a call statement
                            Error(s, "duplicate variable in left-hand side of call statement: {0}", ie.Name);
                    } else {
                        lhsNameSet.Add(ie.Name, null);
                    }
                }
            }
            if (update != null) {
                // figure out what kind of UpdateStmt this is
                if (firstEffectfulRhs == null) {
                    if (s.Lhss.Count == 0) {
                        Contract.Assert(update.Rhss.Count == 1);  // guaranteed by the parser
                        Error(s, "expected method call, found expression");
                    } else if (s.Lhss.Count != update.Rhss.Count) {
                        Error(s, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", s.Lhss.Count, update.Rhss.Count);
                    } else if (arrayRangeLhs != null && s.Lhss.Count != 1) {
                        Error(arrayRangeLhs, "array-range may not be used as LHS of multi-assignment; use separate assignment statements for each array-range assignment");
                    } else if (ErrorCount == prevErrorCount) {
                        // add the statements here in a sequence, but don't use that sequence later for translation (instead, should translated properly as multi-assignment)
                        for (int i = 0; i < s.Lhss.Count; i++) {
                            var a = new AssignStmt(s.Tok, s.Lhss[i].Resolved, update.Rhss[i]);
                            s.ResolvedStatements.Add(a);
                        }
                    }

                } else if (update.CanMutateKnownState) {
                    if (1 < update.Rhss.Count) {
                        Error(firstEffectfulRhs, "cannot have effectful parameter in multi-return statement.");
                    } else { // it might be ok, if it is a TypeRhs
                        Contract.Assert(update.Rhss.Count == 1);
                        if (callRhs != null) {
                            Error(callRhs.Tok, "cannot have method call in return statement.");
                        } else {
                            // we have a TypeRhs
                            Contract.Assert(update.Rhss[0] is TypeRhs);
                            var tr = (TypeRhs)update.Rhss[0];
                            Contract.Assert(tr.InitCall != null); // there were effects, so this must have been a call.
                            if (tr.CanAffectPreviouslyKnownExpressions) {
                                Error(tr.Tok, "can only have initialization methods which modify at most 'this'.");
                            }
                            var a = new AssignStmt(s.Tok, s.Lhss[0].Resolved, tr);
                            s.ResolvedStatements.Add(a);
                        }
                    }

                } else {
                    // if there was an effectful RHS, that must be the only RHS
                    if (update.Rhss.Count != 1) {
                        Error(firstEffectfulRhs, "an update statement is allowed an effectful RHS only if there is just one RHS");
                    } else if (arrayRangeLhs != null) {
                        Error(arrayRangeLhs, "Assignment to range of array elements must have a simple expression RHS; try using a temporary local variable");
                    } else if (callRhs == null) {
                        // must be a single TypeRhs
                        if (s.Lhss.Count != 1) {
                            Contract.Assert(2 <= s.Lhss.Count);  // the parser allows 0 Lhss only if the whole statement looks like an expression (not a TypeRhs)
                            Error(s.Lhss[1].tok, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", s.Lhss.Count, update.Rhss.Count);
                        } else if (ErrorCount == prevErrorCount) {
                            var a = new AssignStmt(s.Tok, s.Lhss[0].Resolved, update.Rhss[0]);
                            s.ResolvedStatements.Add(a);
                        }
                    } else {
                        // a call statement
                        if (ErrorCount == prevErrorCount) {
                            var resolvedLhss = new List<Expression>();
                            foreach (var ll in s.Lhss) {
                                resolvedLhss.Add(ll.Resolved);
                            }
                            var a = new CallStmt(callRhs.Tok, resolvedLhss, callRhs.Receiver, callRhs.MethodName, callRhs.Args);
                            s.ResolvedStatements.Add(a);
                        }
                    }
                }
            }

            foreach (var a in s.ResolvedStatements) {
                ResolveStatement(a, specContextOnly, codeContext);
            }
            s.IsGhost = s.ResolvedStatements.TrueForAll(ss => ss.IsGhost);
        }

        bool ResolveAlternatives(List<GuardedAlternative> alternatives, bool specContextOnly, AlternativeLoopStmt loopToCatchBreaks, ICodeContext codeContext) {
            Contract.Requires(alternatives != null);
            Contract.Requires(codeContext != null);

            bool isGhost = specContextOnly;
            // first, resolve the guards, which tells us whether or not the entire statement is a ghost statement
            foreach (var alternative in alternatives) {
                int prevErrorCount = ErrorCount;
                ResolveExpression(alternative.Guard, true);
                Contract.Assert(alternative.Guard.Type != null);  // follows from postcondition of ResolveExpression
                bool successfullyResolved = ErrorCount == prevErrorCount;
                if (!UnifyTypes(alternative.Guard.Type, Type.Bool)) {
                    Error(alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type);
                }
                if (!specContextOnly && successfullyResolved) {
                    isGhost = isGhost || UsesSpecFeatures(alternative.Guard);
                }
            }

            if (loopToCatchBreaks != null) {
                loopStack.Add(loopToCatchBreaks);  // push
                if (loopToCatchBreaks.Labels == null) {  // otherwise, "loopToCatchBreak" is already in "inSpecOnlyContext" map
                    inSpecOnlyContext.Add(loopToCatchBreaks, specContextOnly);
                }
            }
            foreach (var alternative in alternatives) {
                scope.PushMarker();
                foreach (Statement ss in alternative.Body) {
                    ResolveStatement(ss, isGhost, codeContext);
                }
                scope.PopMarker();
            }
            if (loopToCatchBreaks != null) {
                loopStack.RemoveAt(loopStack.Count - 1);  // pop
            }

            return isGhost;
        }

        /// <summary>
        /// Resolves the given call statement.
        /// Assumes all LHSs have already been resolved (and checked for mutability).
        /// </summary>
        void ResolveCallStmt(CallStmt s, bool specContextOnly, ICodeContext codeContext, Type receiverType) {
            Contract.Requires(s != null);
            Contract.Requires(codeContext != null);
            bool isInitCall = receiverType != null;

            // resolve receiver
            ResolveReceiver(s.Receiver, true);
            Contract.Assert(s.Receiver.Type != null);  // follows from postcondition of ResolveExpression
            if (receiverType == null) {
                receiverType = s.Receiver.Type;
            }
            // resolve the method name
            NonProxyType nptype;
            MemberDecl member = ResolveMember(s.Tok, receiverType, s.MethodName, out nptype);
            Method callee = null;
            if (member == null) {
                // Yuyan
                // error has already been reported by ResolveMember
                // otherwise it is a wildcard expression, which is not allowed here. An error should report
                Error(s.Tok, "Wildcard expression is not allowed in a call statement");
            } else if (member is Method) {
                s.Method = (Method)member;
                callee = s.Method;
                if (!isInitCall && callee is Constructor) {
                    Error(s, "a constructor is only allowed to be called when an object is being allocated");
                }
                s.IsGhost = callee.IsGhost;
                if (specContextOnly && !callee.IsGhost) {
                    Error(s, "only ghost methods can be called from this context");
                }
            } else {
                Error(s, "member {0} in type {1} does not refer to a method", s.MethodName, nptype);
            }

            // resolve left-hand sides
            foreach (var lhs in s.Lhs) {
                Contract.Assume(lhs.Type != null);  // a sanity check that LHSs have already been resolved
            }
            // resolve arguments
            if (!s.IsGhost && s.Receiver.WasResolved()) {
                CheckIsNonGhost(s.Receiver);
            }
            int j = 0;
            foreach (Expression e in s.Args) {
                bool allowGhost = s.IsGhost || callee == null || callee.Ins.Count <= j || callee.Ins[j].IsGhost;
                ResolveExpression(e, true);
                if (!allowGhost) {
                    CheckIsNonGhost(e);
                }
                j++;
            }

            if (callee == null) {
                // error has been reported above
            } else if (callee.Ins.Count != s.Args.Count) {
                Error(s, "wrong number of method arguments (got {0}, expected {1})", s.Args.Count, callee.Ins.Count);
            } else if (callee.Outs.Count != s.Lhs.Count) {
                if (isInitCall) {
                    Error(s, "a method called as an initialization method must not have any result arguments");
                } else {
                    Error(s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, callee.Outs.Count);
                }
            } else {
                Contract.Assert(nptype != null);  // follows from postcondition of ResolveMember above
                if (isInitCall) {
                    if (callee.IsStatic) {
                        Error(s.Tok, "a method called as an initialization method must not be 'static'");
                    }
                } else if (!callee.IsStatic) {
                    if (!scope.AllowInstance && s.Receiver is ThisExpr) {
                        // The call really needs an instance, but that instance is given as 'this', which is not
                        // available in this context.  For more details, see comment in the resolution of a
                        // FunctionCallExpr.
                        Error(s.Receiver, "'this' is not allowed in a 'static' context");
                    } else if (s.Receiver is StaticReceiverExpr) {
                        Error(s.Receiver, "call to instance method requires an instance");
                    }
                }
#if !NO_WORK_TO_BE_DONE
                UserDefinedType ctype = (UserDefinedType)nptype;  // TODO: get rid of this statement, make this code handle any non-proxy type
#endif
                // build the type substitution map
                s.TypeArgumentSubstitutions = new Dictionary<TypeParameter, Type>();
                for (int i = 0; i < ctype.TypeArgs.Count; i++) {
                    s.TypeArgumentSubstitutions.Add(cce.NonNull(ctype.ResolvedClass).TypeArgs[i], ctype.TypeArgs[i]);
                }
                foreach (TypeParameter p in callee.TypeArgs) {
                    s.TypeArgumentSubstitutions.Add(p, new ParamTypeProxy(p));
                }
                // type check the arguments
                for (int i = 0; i < callee.Ins.Count; i++) {
                    Type st = SubstType(callee.Ins[i].Type, s.TypeArgumentSubstitutions);
                    if (!UnifyTypes(cce.NonNull(s.Args[i].Type), st)) {
                        Error(s, "incorrect type of method in-parameter {0} (expected {1}, got {2})", i, st, s.Args[i].Type);
                    }
                }
                for (int i = 0; i < callee.Outs.Count; i++) {
                    Type st = SubstType(callee.Outs[i].Type, s.TypeArgumentSubstitutions);
                    var lhs = s.Lhs[i];
                    if (!UnifyTypes(cce.NonNull(lhs.Type), st)) {
                        Error(s, "incorrect type of method out-parameter {0} (expected {1}, got {2})", i, st, lhs.Type);
                    } else {
                        var resolvedLhs = lhs.Resolved;
                        if (!specContextOnly && (s.IsGhost || callee.Outs[i].IsGhost)) {
                            // LHS must denote a ghost
                            if (resolvedLhs is IdentifierExpr) {
                                var ll = (IdentifierExpr)resolvedLhs;
                                if (!ll.Var.IsGhost) {
                                    if (ll is AutoGhostIdentifierExpr && ll.Var is VarDecl) {
                                        // the variable was actually declared in this statement, so auto-declare it as ghost
                                        ((VarDecl)ll.Var).MakeGhost();
                                    } else {
                                        Error(s, "actual out-parameter {0} is required to be a ghost variable", i);
                                    }
                                }
                            } else if (resolvedLhs is FieldSelectExpr) {
                                var ll = (FieldSelectExpr)resolvedLhs;
                                if (!ll.Field.IsGhost) {
                                    Error(s, "actual out-parameter {0} is required to be a ghost field", i);
                                }
                            } else {
                                // this is an array update, and arrays are always non-ghost
                                Error(s, "actual out-parameter {0} is required to be a ghost variable", i);
                            }
                        }
                        // LHS must denote a mutable field.
                        if (resolvedLhs is IdentifierExpr) {
                            var ll = (IdentifierExpr)resolvedLhs;
                            if (!ll.Var.IsMutable) {
                                Error(resolvedLhs, "LHS of assignment must denote a mutable variable");
                            }
                        } else if (resolvedLhs is FieldSelectExpr) {
                            var ll = (FieldSelectExpr)resolvedLhs;
                            if (!ll.Field.IsUserMutable) {
                                Error(resolvedLhs, "LHS of assignment must denote a mutable field");
                            }
                        }
                    }
                }

                // Resolution termination check
                ModuleDefinition callerModule = codeContext.EnclosingModule;
                ModuleDefinition calleeModule = ((ICodeContext)callee).EnclosingModule;
                if (callerModule == calleeModule) {
                    // intra-module call; this is allowed; add edge in module's call graph
                    var caller = codeContext is Method ? (Method)codeContext : ((IteratorDecl)codeContext).Member_MoveNext;
                    callerModule.CallGraph.AddEdge(caller, callee);
                } else {
                    //Contract.Assert(dependencies.Reaches(callerModule, calleeModule));
                    //
                }
            }
        }

        void ResolveBlockStatement(BlockStmt blockStmt, bool specContextOnly, ICodeContext codeContext) {
            Contract.Requires(blockStmt != null);
            Contract.Requires(codeContext != null);

            foreach (Statement ss in blockStmt.Body) {
                labeledStatements.PushMarker();
                // push labels
                for (var l = ss.Labels; l != null; l = l.Next) {
                    var lnode = l.Data;
                    Contract.Assert(lnode.Name != null);  // LabelNode's with .Label==null are added only during resolution of the break statements with 'stmt' as their target, which hasn't happened yet
                    var prev = labeledStatements.Find(lnode.Name);
                    if (prev == ss) {
                        Error(lnode.Tok, "duplicate label");
                    } else if (prev != null) {
                        Error(lnode.Tok, "label shadows an enclosing label");
                    } else {
                        bool b = labeledStatements.Push(lnode.Name, ss);
                        Contract.Assert(b);  // since we just checked for duplicates, we expect the Push to succeed
                        if (l == ss.Labels) {  // add it only once
                            inSpecOnlyContext.Add(ss, specContextOnly);
                        }
                    }
                }
                ResolveStatement(ss, specContextOnly, codeContext);
                labeledStatements.PopMarker();
            }
        }

        /// <summary>
        /// This method performs some additional checks on the body "stmt" of a parallel statement of kind "kind".
        /// </summary>
        public void CheckParallelBodyRestrictions(Statement stmt, ParallelStmt.ParBodyKind kind) {
            Contract.Requires(stmt != null);
            if (stmt is PredicateStmt) {
                // cool
            } else if (stmt is PrintStmt) {
                Error(stmt, "print statement is not allowed inside a parallel statement");
            } else if (stmt is BreakStmt) {
                // this case is checked already in the first pass through the parallel body, by doing so from an empty set of labeled statements and resetting the loop-stack
            } else if (stmt is ReturnStmt) {
                Error(stmt, "return statement is not allowed inside a parallel statement");
            } else if (stmt is YieldStmt) {
                Error(stmt, "yield statement is not allowed inside a parallel statement");
            } else if (stmt is AssignSuchThatStmt) {
                var s = (AssignSuchThatStmt)stmt;
                foreach (var lhs in s.Lhss) {
                    CheckParallelBodyLhs(s.Tok, lhs.Resolved, kind);
                }
            } else if (stmt is ConcreteSyntaxStatement) {
                var s = (ConcreteSyntaxStatement)stmt;
                foreach (var ss in s.ResolvedStatements) {
                    CheckParallelBodyRestrictions(ss, kind);
                }
            } else if (stmt is AssignStmt) {
                var s = (AssignStmt)stmt;
                CheckParallelBodyLhs(s.Tok, s.Lhs.Resolved, kind);
                var rhs = s.Rhs;  // ExprRhs and HavocRhs are fine, but TypeRhs is not
                if (rhs is TypeRhs) {
                    if (kind == ParallelStmt.ParBodyKind.Assign) {
                        Error(rhs.Tok, "new allocation not supported in parallel statements");
                    } else {
                        var t = (TypeRhs)rhs;
                        if (t.InitCall != null) {
                            CheckParallelBodyRestrictions(t.InitCall, kind);
                        }
                    }
                } else if (rhs is ExprRhs) {
                    var r = ((ExprRhs)rhs).Expr.Resolved;
                    if (kind == ParallelStmt.ParBodyKind.Assign && r is UnaryExpr && ((UnaryExpr)r).Op == UnaryExpr.Opcode.SetChoose) {
                        Error(r, "set choose operator not supported inside the enclosing parallel statement");
                    }
                }
            } else if (stmt is VarDecl) {
                // cool
            } else if (stmt is CallStmt) {
                var s = (CallStmt)stmt;
                foreach (var lhs in s.Lhs) {
                    var idExpr = lhs as IdentifierExpr;
                    if (idExpr != null) {
                        if (scope.ContainsDecl(idExpr.Var)) {
                            Error(stmt, "body of parallel statement is attempting to update a variable declared outside the parallel statement");
                        }
                    } else {
                        Error(stmt, "the body of the enclosing parallel statement is not allowed to update heap locations");
                    }
                }
                if (!s.Method.IsGhost) {
                    // The reason for this restriction is that the compiler is going to omit the parallel statement altogether--it has
                    // no effect.  However, print effects are not documented, so to make sure that the compiler does not omit a call to
                    // a method that prints something, all calls to non-ghost methods are disallowed.  (Note, if this restriction
                    // is somehow lifted in the future, then it is still necessary to enforce s.Method.Mod.Expressions.Count != 0 for
                    // calls to non-ghost methods.)
                    Error(s, "the body of the enclosing parallel statement is not allowed to call non-ghost methods");
                }

            } else if (stmt is BlockStmt) {
                var s = (BlockStmt)stmt;
                scope.PushMarker();
                foreach (var ss in s.Body) {
                    CheckParallelBodyRestrictions(ss, kind);
                }
                scope.PopMarker();

            } else if (stmt is IfStmt) {
                var s = (IfStmt)stmt;
                CheckParallelBodyRestrictions(s.Thn, kind);
                if (s.Els != null) {
                    CheckParallelBodyRestrictions(s.Els, kind);
                }

            } else if (stmt is AlternativeStmt) {
                var s = (AlternativeStmt)stmt;
                foreach (var alt in s.Alternatives) {
                    foreach (var ss in alt.Body) {
                        CheckParallelBodyRestrictions(ss, kind);
                    }
                }

            } else if (stmt is WhileStmt) {
                WhileStmt s = (WhileStmt)stmt;
                CheckParallelBodyRestrictions(s.Body, kind);

            } else if (stmt is AlternativeLoopStmt) {
                var s = (AlternativeLoopStmt)stmt;
                foreach (var alt in s.Alternatives) {
                    foreach (var ss in alt.Body) {
                        CheckParallelBodyRestrictions(ss, kind);
                    }
                }

            } else if (stmt is ParallelStmt) {
                var s = (ParallelStmt)stmt;
                switch (s.Kind) {
                    case ParallelStmt.ParBodyKind.Assign:
                        Error(stmt, "a parallel statement with heap updates is not allowed inside the body of another parallel statement");
                        break;
                    case ParallelStmt.ParBodyKind.Call:
                    case ParallelStmt.ParBodyKind.Proof:
                        // these are fine, since they don't update any non-local state
                        break;
                    default:
                        Contract.Assert(false);  // unexpected kind
                        break;
                }

            } else if (stmt is CalcStmt) {
                // cool
                // NadiaTodo: ...I assume because it's always ghost		

            } else if (stmt is MatchStmt) {
                var s = (MatchStmt)stmt;
                foreach (var kase in s.Cases) {
                    foreach (var ss in kase.Body) {
                        CheckParallelBodyRestrictions(ss, kind);
                    }
                }

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();
            }
        }

        void CheckParallelBodyLhs(IToken tok, Expression lhs, ParallelStmt.ParBodyKind kind) {
            var idExpr = lhs as IdentifierExpr;
            if (idExpr != null) {
                if (scope.ContainsDecl(idExpr.Var)) {
                    Error(tok, "body of parallel statement is attempting to update a variable declared outside the parallel statement");
                }
            } else if (kind != ParallelStmt.ParBodyKind.Assign) {
                Error(tok, "the body of the enclosing parallel statement is not allowed to update heap locations");
            }
        }

        Type ResolveTypeRhs(TypeRhs rr, Statement stmt, bool specContextOnly, ICodeContext codeContext) {
            Contract.Requires(rr != null);
            Contract.Requires(stmt != null);
            Contract.Requires(codeContext != null);
            Contract.Ensures(Contract.Result<Type>() != null);

            if (rr.Type == null) {
                ResolveType(stmt.Tok, rr.EType, null, true);
                if (rr.ArrayDimensions == null) {
                    if (!rr.EType.IsRefType) {
                        Error(stmt, "new can be applied only to reference types (got {0})", rr.EType);
                    } else {
                        bool callsConstructor = false;
                        if (rr.InitCall != null) {
                            ResolveCallStmt(rr.InitCall, specContextOnly, codeContext, rr.EType);
                            if (rr.InitCall.Method is Constructor) {
                                callsConstructor = true;
                            }
                        }
                        if (!callsConstructor && rr.EType is UserDefinedType) {
                            var udt = (UserDefinedType)rr.EType;
                            var cl = (ClassDecl)udt.ResolvedClass;  // cast is guaranteed by the call to rr.EType.IsRefType above, together with the "rr.EType is UserDefinedType" test
                            if (cl.HasConstructor) {
                                Error(stmt, "when allocating an object of type '{0}', one of its constructor methods must be called", cl.Name);
                            }
                        }
                    }
                    rr.Type = rr.EType;
                } else {
                    int i = 0;
                    if (rr.EType.IsSubrangeType) {
                        Error(stmt, "sorry, cannot instantiate 'array' type with a subrange type");
                    }
                    foreach (Expression dim in rr.ArrayDimensions) {
                        Contract.Assert(dim != null);
                        ResolveExpression(dim, true);
                        if (!UnifyTypes(dim.Type, Type.Int)) {
                            Error(stmt, "new must use an integer expression for the array size (got {0} for index {1})", dim.Type, i);
                        }
                        i++;
                    }
                    rr.Type = builtIns.ArrayType(rr.ArrayDimensions.Count, rr.EType);
                }
            }
            return rr.Type;
        }

        MemberDecl ResolveMember(IToken tok, Type receiverType, string memberName, out NonProxyType nptype) {
            Contract.Requires(tok != null);
            Contract.Requires(receiverType != null);
            Contract.Requires(memberName != null);
            Contract.Ensures(Contract.Result<MemberDecl>() == null || Contract.ValueAtReturn(out nptype) != null);

            nptype = null;  // prepare for the worst
            receiverType = receiverType.Normalize();
            if (receiverType is TypeProxy) {
                Error(tok, "type of the receiver is not fully determined at this program point", receiverType);
                return null;
            }
            Contract.Assert(receiverType is NonProxyType);  // there are only two kinds of types: proxies and non-proxies

            UserDefinedType ctype = UserDefinedType.DenotesClass(receiverType);
            if (ctype != null) {
                var cd = (ClassDecl)ctype.ResolvedClass;  // correctness of cast follows from postcondition of DenotesClass
                Contract.Assert(ctype.TypeArgs.Count == cd.TypeArgs.Count);  // follows from the fact that ctype was resolved
                MemberDecl member = null; // Yuyan: initialized it to null
                if (memberName == "*") { // Yuyan:  wildcard
                    nptype = ctype;
                    return null;
                } else if (!classMembers[cd].TryGetValue(memberName, out member)) {
                    Error(tok, "member {0} does not exist in {2} {1}", memberName, ctype.Name, cd is IteratorDecl ? "iterator" : "class");
                    return null;
                } else {
                    nptype = ctype;
                    return member;
                }
            }

            DatatypeDecl dtd = receiverType.AsDatatype;
            if (dtd != null) {
                MemberDecl member;
                if (memberName == "*") { // Yuyan 
                    return null;
                } else if (!datatypeMembers[dtd].TryGetValue(memberName, out member)) {
                    Error(tok, "member {0} does not exist in datatype {1}", memberName, dtd.Name);
                    return null;
                } else {
                    nptype = (UserDefinedType)receiverType;
                    return member;
                }
            }

            Error(tok, "type {0} does not have a member {1}", receiverType, memberName);
            return null;
        }

        public static Type SubstType(Type type, Dictionary<TypeParameter/*!*/, Type/*!*/>/*!*/ subst) {
            Contract.Requires(type != null);
            Contract.Requires(cce.NonNullDictionaryAndValues(subst));
            Contract.Ensures(Contract.Result<Type>() != null);

            if (type is BasicType) {
                return type;
            } else if (type is MapType) {
                MapType t = (MapType)type;
                return new MapType(SubstType(t.Domain, subst), SubstType(t.Range, subst));
            } else if (type is CollectionType) {
                CollectionType t = (CollectionType)type;
                Type arg = SubstType(t.Arg, subst);
                if (arg == t.Arg) {
                    return type;
                } else if (type is SetType) {
                    return new SetType(arg);
                } else if (type is MultiSetType) {
                    return new MultiSetType(arg);
                } else if (type is SeqType) {
                    return new SeqType(arg);
                } else {
                    Contract.Assert(false); throw new cce.UnreachableException();  // unexpected collection type
                }
            } else if (type is UserDefinedType) {
                UserDefinedType t = (UserDefinedType)type;
                if (t.ResolvedParam != null) {
                    Contract.Assert(t.TypeArgs.Count == 0);
                    Type s;
                    if (subst.TryGetValue(t.ResolvedParam, out s)) {
                        return cce.NonNull(s);
                    } else {
                        return type;
                    }
                } else if (t.ResolvedClass != null) {
                    List<Type> newArgs = null;  // allocate it lazily
                    for (int i = 0; i < t.TypeArgs.Count; i++) {
                        Type p = t.TypeArgs[i];
                        Type s = SubstType(p, subst);
                        if (s != p && newArgs == null) {
                            // lazily construct newArgs
                            newArgs = new List<Type>();
                            for (int j = 0; j < i; j++) {
                                newArgs.Add(t.TypeArgs[j]);
                            }
                        }
                        if (newArgs != null) {
                            newArgs.Add(s);
                        }
                    }
                    if (newArgs == null) {
                        // there were no substitutions
                        return type;
                    } else {
                        return new UserDefinedType(t.tok, t.Name, t.ResolvedClass, newArgs);
                    }
                } else {
                    // there's neither a resolved param nor a resolved class, which means the UserDefinedType wasn't
                    // properly resolved; just return it
                    return type;
                }
            } else if (type is TypeProxy) {
                TypeProxy t = (TypeProxy)type;
                if (t.T == null) {
                    return type;
                } else {
                    // bypass the proxy
                    return SubstType(t.T, subst);
                }
            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected type
            }
        }

        public static UserDefinedType GetThisType(IToken tok, ClassDecl cl) {
            Contract.Requires(tok != null);
            Contract.Requires(cl != null);
            Contract.Ensures(Contract.Result<UserDefinedType>() != null);

            List<Type> args = new List<Type>();
            foreach (TypeParameter tp in cl.TypeArgs) {
                args.Add(new UserDefinedType(tok, tp.Name, tp));
            }
            return new UserDefinedType(tok, cl.Name, cl, args);
        }

        /// <summary>
        /// Requires "member" to be declared in a class.
        /// </summary>
        public static UserDefinedType GetReceiverType(IToken tok, MemberDecl member) {
            Contract.Requires(tok != null);
            Contract.Requires(member != null);
            Contract.Ensures(Contract.Result<UserDefinedType>() != null);

            return GetThisType(tok, (ClassDecl)member.EnclosingClass);
        }

        /// <summary>
        /// "twoState" implies that "old" and "fresh" expressions are allowed.
        /// </summary>
        void ResolveExpression(Expression expr, bool twoState) {
            ResolveExpression(expr, twoState, null);
        }

        /// <summary>
        /// "matchVarContext" says which variables are allowed to be used as the source expression in a "match" expression;
        /// if null, no "match" expression will be allowed.
        /// </summary>
        void ResolveExpression(Expression expr, bool twoState, List<IVariable> matchVarContext) {
            Contract.Requires(expr != null);
            Contract.Ensures(expr.Type != null);
            if (expr.Type != null) {
                // expression has already been resovled
                return;
            }

            // The following cases will resolve the subexpressions and will attempt to assign a type of expr.  However, if errors occur
            // and it cannot be determined what the type of expr is, then it is fine to leave expr.Type as null.  In that case, the end
            // of this method will assign proxy type to the expression, which reduces the number of error messages that are produced
            // while type checking the rest of the program.

            if (expr is ParensExpression) {
                var e = (ParensExpression)expr;
                ResolveExpression(e.E, twoState, matchVarContext);  // allow "match" expressions inside e.E if the parenthetic expression had been allowed to be a "match" expression
                e.ResolvedExpression = e.E;
                e.Type = e.E.Type;

            } else if (expr is ChainingExpression) {
                var e = (ChainingExpression)expr;
                ResolveExpression(e.E, twoState);
                e.ResolvedExpression = e.E;
                e.Type = e.E.Type;

            } else if (expr is IdentifierSequence) {
                var e = (IdentifierSequence)expr;
                ResolveIdentifierSequence(e, twoState, false);

            } else if (expr is LiteralExpr) {
                LiteralExpr e = (LiteralExpr)expr;
                if (e.Value == null) {
                    e.Type = new ObjectTypeProxy();
                } else if (e.Value is BigInteger) {
                    e.Type = Type.Int;
                } else if (e.Value is bool) {
                    e.Type = Type.Bool;
                } else {
                    Contract.Assert(false); throw new cce.UnreachableException();  // unexpected literal type
                }

            } else if (expr is ThisExpr) {
                if (!scope.AllowInstance) {
                    Error(expr, "'this' is not allowed in a 'static' context");
                }
                if (currentClass != null) {
                    expr.Type = GetThisType(expr.tok, currentClass);  // do this regardless of scope.AllowInstance, for better error reporting
                    GenerateRegionList(expr); // Yuyan
                }

            } else if (expr is IdentifierExpr) {
                IdentifierExpr e = (IdentifierExpr)expr;
                e.Var = scope.Find(e.Name);
                if (e.Var == null) {
                    Error(expr, "Identifier does not denote a local variable, parameter, or bound variable: {0}", e.Name);
                } else {
                    expr.Type = e.Var.Type;
                }
            } else if (expr is PatternExpression) { // Yuyan
                PatternExpression e = (PatternExpression)expr;
                if (e.E.Type == null) {
                    scope.Push(e.E.tok.val, new Formal(e.E.tok, e.E.tok.val, Type.Flexible, false, true));    // not need to check the duplicity. it is done somewhere else
                } else {
                    scope.Push(e.E.tok.val, new Formal(e.E.tok, e.E.tok.val, e.E.Type, false, true));
                    e.Type = e.E.Type;
                    e.E = new IdentifierExpr(e.E.tok, e.E.tok.val); // Resolve it again;
                }

                ResolveExpression(e.E, twoState);
                // the type is resolved somewhere else
                // give a flexible type temporarily
                if (e.Type == null) {
                    e.Type = Type.Flexible;
                }

            } else if (expr is ClassExpression) {     // Yuyan: added ClassExpression used in the region-filter-expression
                ClassExpression ce = (ClassExpression)expr;
                TopLevelDecl d;
                if (!moduleInfo.TopLevels.TryGetValue(ce.ClassName, out d)) {
                    Error(expr.tok, "Undeclared datatype: {0}", ce.ClassName);
                } else if (d is AmbiguousTopLevelDecl) {
                    Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1}", ce.ClassName, ((AmbiguousTopLevelDecl)d).ModuleNames());
                } else if (d is ClassDecl) {
                    ce.ClsDecl = (ClassDecl)d;
                } else {
                    Error(expr.tok, "Expected class name: {0}", ce.ClassName);
                }
            } else if (expr is FieldExpression) {  // Yuyan: added FieldExpression used in the region-filter-expression

                // nothing can do, resolve it in resolving region-filter-expression

            } else if (expr is RegionConstructExpression) { // Yuyan: added to resolve RegionConstructExpression
                RegionConstructExpression rce = (RegionConstructExpression)expr;
                ResolveExpression(rce.E, twoState);
                Type t = rce.E.Type;
                Contract.Assert(t != null);  // follows from postcondition of ResolveExpression
                ResolveRegionExpression(rce);
                expr.Type = new RegionType();
            } else if (expr is RegionFilterExpression) { // Yuyan: added to resolve RegionFiltertExpression
                RegionFilterExpression rfe = (RegionFilterExpression)expr;
                ResolveExpression(rfe.regionE, twoState);
                foreach (Expression filter in rfe.filterList) {
                    ResolveExpression(filter, twoState);
                }
                rfe.filterType = ResolveFilterType(rfe.filterList);
                expr.Type = new RegionType();
            }else if (expr is FootprintExpression){  // Yuyan
                FootprintExpression fe = (FootprintExpression)expr;
                ResolveExpression(fe.E, twoState);
                fe.Footprint = ResolveFootprintExpression(fe.E);
                foreach (RegionConstructExpression rce in fe.Footprint) {
                    ResolveRegionExpression(rce);
                }
                expr.Type = new RegionType();
            } else if (expr is DatatypeValue) {
                DatatypeValue dtv = (DatatypeValue)expr;
                TopLevelDecl d;
                if (!moduleInfo.TopLevels.TryGetValue(dtv.DatatypeName, out d)) {
                    Error(expr.tok, "Undeclared datatype: {0}", dtv.DatatypeName);
                } else if (d is AmbiguousTopLevelDecl) {
                    Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1}", dtv.DatatypeName, ((AmbiguousTopLevelDecl)d).ModuleNames());
                } else if (!(d is DatatypeDecl)) {
                    Error(expr.tok, "Expected datatype: {0}", dtv.DatatypeName);
                } else {
                    ResolveDatatypeValue(twoState, dtv, (DatatypeDecl)d);
                }

            } else if (expr is DisplayExpression) {
                DisplayExpression e = (DisplayExpression)expr;
                Type elementType = new InferredTypeProxy();
                foreach (Expression ee in e.Elements) {
                    ResolveExpression(ee, twoState);
                    Contract.Assert(ee.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(elementType, ee.Type)) {
                        Error(ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType);
                    }
                }
                if (expr is SetDisplayExpr) {
                    expr.Type = new SetType(elementType);
                } else if (expr is MultiSetDisplayExpr) {
                    expr.Type = new MultiSetType(elementType);
                } else {
                    expr.Type = new SeqType(elementType);
                }
            } else if (expr is MapDisplayExpr) {
                MapDisplayExpr e = (MapDisplayExpr)expr;
                Type domainType = new InferredTypeProxy();
                Type rangeType = new InferredTypeProxy();
                foreach (ExpressionPair p in e.Elements) {
                    ResolveExpression(p.A, twoState);
                    Contract.Assert(p.A.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(domainType, p.A.Type)) {
                        Error(p.A, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.A.Type, domainType);
                    }
                    ResolveExpression(p.B, twoState);
                    Contract.Assert(p.B.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(rangeType, p.B.Type)) {
                        Error(p.B, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.B.Type, rangeType);
                    }
                }
                expr.Type = new MapType(domainType, rangeType);
            } else if (expr is ExprDotName) {
                var e = (ExprDotName)expr;
                // The following call to ResolveExpression is just preliminary.  If it succeeds, it is redone below on the resolved expression.  Thus,
                // it's okay to be more lenient here and use coLevel (instead of trying to use CoLevel_Dec(coLevel), which is needed when .Name denotes a
                // destructor for a co-datatype).
                ResolveExpression(e.Obj, twoState);
                Contract.Assert(e.Obj.Type != null);  // follows from postcondition of ResolveExpression
                Expression resolved = ResolvePredicateOrField(expr.tok, e.Obj, e.SuffixName);
                if (resolved == null) {
                    // error has already been reported by ResolvePredicateOrField
                } else {
                    // the following will cause e.Obj to be resolved again, but that's still correct
                    e.ResolvedExpression = resolved;
                    ResolveExpression(e.ResolvedExpression, twoState);
                    e.Type = e.ResolvedExpression.Type;
                }

            } else if (expr is FieldSelectExpr) {
                var e = (FieldSelectExpr)expr;
                ResolveExpression(e.Obj, twoState);
                Contract.Assert(e.Obj.Type != null);  // follows from postcondition of ResolveExpression
                NonProxyType nptype;
                MemberDecl member = ResolveMember(expr.tok, e.Obj.Type, e.FieldName, out nptype);
#if !NO_WORK_TO_BE_DONE
                UserDefinedType ctype = (UserDefinedType)nptype;
#endif
                if (member == null) {
                    // Yuyan: it must be a wildcard expression
                    // otherwise error has already been reported by ResolveMember
                    e.Field = new Field(expr.tok, expr.tok.val, false, Type.Flexible, null);
                } else if (!(member is Field)) {
                    Error(expr, "member {0} in type {1} does not refer to a field", e.FieldName, cce.NonNull(ctype).Name);
                } else {
                    Contract.Assert(ctype != null && ctype.ResolvedClass != null);  // follows from postcondition of ResolveMember
                    e.Field = (Field)member;
                    if (e.Obj is StaticReceiverExpr) {
                        Error(expr, "a field must be selected via an object, not just a class name");
                    }
                    // build the type substitution map
                    Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
                    for (int i = 0; i < ctype.TypeArgs.Count; i++) {
                        subst.Add(ctype.ResolvedClass.TypeArgs[i], ctype.TypeArgs[i]);
                    }
                    e.Type = SubstType(e.Field.Type, subst);
                }

            } else if (expr is SeqSelectExpr) {
                SeqSelectExpr e = (SeqSelectExpr)expr;
                ResolveSeqSelectExpr(e, twoState, true);

            } else if (expr is MultiSelectExpr) {
                MultiSelectExpr e = (MultiSelectExpr)expr;

                ResolveExpression(e.Array, twoState);
                Contract.Assert(e.Array.Type != null);  // follows from postcondition of ResolveExpression
                Type elementType = new InferredTypeProxy();
                if (!UnifyTypes(e.Array.Type, builtIns.ArrayType(e.Indices.Count, elementType))) {
                    Error(e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type);
                }
                int i = 0;
                foreach (Expression idx in e.Indices) {
                    Contract.Assert(idx != null);
                    ResolveExpression(idx, twoState);
                    Contract.Assert(idx.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(idx.Type, Type.Int)) {
                        Error(idx, "array selection requires integer indices (got {0} for index {1})", idx.Type, i);
                    }
                    i++;
                }
                e.Type = elementType;

            } else if (expr is SeqUpdateExpr) {
                SeqUpdateExpr e = (SeqUpdateExpr)expr;
                ResolveExpression(e.Seq, twoState);
                Contract.Assert(e.Seq.Type != null);  // follows from postcondition of ResolveExpression
                Type elementType = new InferredTypeProxy();
                Type domainType = new InferredTypeProxy();
                Type rangeType = new InferredTypeProxy();
                if (UnifyTypes(e.Seq.Type, new SeqType(elementType))) {
                    ResolveExpression(e.Index, twoState);
                    Contract.Assert(e.Index.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(e.Index.Type, Type.Int)) {
                        Error(e.Index, "sequence update requires integer index (got {0})", e.Index.Type);
                    }
                    ResolveExpression(e.Value, twoState);
                    Contract.Assert(e.Value.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(e.Value.Type, elementType)) {
                        Error(e.Value, "sequence update requires the value to have the element type of the sequence (got {0})", e.Value.Type);
                    }
                    expr.Type = e.Seq.Type;
                } else if (UnifyTypes(e.Seq.Type, new MapType(domainType, rangeType))) {
                    ResolveExpression(e.Index, twoState);
                    if (!UnifyTypes(e.Index.Type, domainType)) {
                        Error(e.Index, "map update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type);
                    }
                    ResolveExpression(e.Value, twoState);
                    if (!UnifyTypes(e.Value.Type, rangeType)) {
                        Error(e.Value, "map update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type);
                    }
                    expr.Type = e.Seq.Type;
                } else {
                    Error(expr, "update requires a sequence or map (got {0})", e.Seq.Type);
                }


            } else if (expr is FunctionCallExpr) {
                FunctionCallExpr e = (FunctionCallExpr)expr;
                ResolveFunctionCallExpr(e, twoState, false);

            } else if (expr is OldExpr) {
                OldExpr e = (OldExpr)expr;
                if (!twoState) {
                    Error(expr, "old expressions are not allowed in this context");
                }
                ResolveExpression(e.E, twoState, null);
                expr.Type = e.E.Type;

            } else if (expr is MultiSetFormingExpr) {
                MultiSetFormingExpr e = (MultiSetFormingExpr)expr;
                ResolveExpression(e.E, twoState);
                if (!UnifyTypes(e.E.Type, new SetType(new InferredTypeProxy())) && !UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy()))) {
                    Error(e.tok, "can only form a multiset from a seq or set.");
                }
                expr.Type = new MultiSetType(((CollectionType)e.E.Type).Arg);
            } else if (expr is FreshExpr) {
                FreshExpr e = (FreshExpr)expr;
                if (!twoState) {
                    Error(expr, "fresh expressions are not allowed in this context");
                }
                ResolveExpression(e.E, twoState);
                // the type of e.E must be either an object or a collection of objects
                Type t = e.E.Type;
                Contract.Assert(t != null);  // follows from postcondition of ResolveExpression
                if (t is CollectionType) {
                    t = ((CollectionType)t).Arg;
                }
                if (t is ObjectType) {
                    // fine
                } else if (UserDefinedType.DenotesClass(t) != null) {
                    // fine
                } else if (t.IsDatatype) {
                    // fine, treat this as the datatype itself.
                } else if (t is RegionType) {
                    // Yuyan: fine
                } else {
                    Error(expr, "the argument of a fresh expression must denote an object or a collection of objects (instead got {0})", e.E.Type);
                }
                expr.Type = Type.Bool;

            } else if (expr is UnaryExpr) {
                UnaryExpr e = (UnaryExpr)expr;
                ResolveExpression(e.E, twoState);
                Contract.Assert(e.E.Type != null);  // follows from postcondition of ResolveExpression
                switch (e.Op) {
                    case UnaryExpr.Opcode.Not:
                        if (!UnifyTypes(e.E.Type, Type.Bool)) {
                            Error(expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type);
                        }
                        expr.Type = Type.Bool;
                        break;
                    case UnaryExpr.Opcode.SetChoose:
                        var elType = new InferredTypeProxy();
                        if (!UnifyTypes(e.E.Type, new SetType(elType))) {
                            Error(expr, "choose operator expects a set argument (instead got {0})", e.E.Type);
                        }
                        expr.Type = elType;
                        break;
                    case UnaryExpr.Opcode.Length:
                        if (!UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy())) &&
                            !UnifyTypes(e.E.Type, new RegionType())) {
                            Error(expr, "length operator expects a sequence or region argument (instead got {0})", e.E.Type);
                        }
                        expr.Type = Type.Int;
                        break;
                    default:
                        Contract.Assert(false); throw new cce.UnreachableException();  // unexpected unary operator
                }

                e.ResolvedOp = ResolveOp(e.Op, e.E.Type);

            } else if (expr is BinaryExpr) {
                BinaryExpr e = (BinaryExpr)expr;
                ResolveExpression(e.E0, twoState);
                Contract.Assert(e.E0.Type != null);  // follows from postcondition of ResolveExpression
                ResolveExpression(e.E1, twoState);
                Contract.Assert(e.E1.Type != null);  // follows from postcondition of ResolveExpression
                // Yuyan: resolve pattern expression here
                ResolvePatternExpression(e);
                switch (e.Op) {
                    case BinaryExpr.Opcode.Iff:
                    case BinaryExpr.Opcode.Imp:
                    case BinaryExpr.Opcode.And:
                    case BinaryExpr.Opcode.Or:
                        if (!UnifyTypes(e.E0.Type, Type.Bool)) {
                            Error(expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
                        }
                        if (!UnifyTypes(e.E1.Type, Type.Bool)) {
                            Error(expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
                        }
                        expr.Type = Type.Bool;
                        break;

                    case BinaryExpr.Opcode.Eq:
                    case BinaryExpr.Opcode.Neq:
                        if (!ComparableTypes(e.E0.Type, e.E1.Type)) {
                            if (!UnifyTypes(e.E0.Type, e.E1.Type)) {
                                Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
                            }
                        }
                        expr.Type = Type.Bool;
                        break;

                    case BinaryExpr.Opcode.Disjoint:
                        // TODO: the error messages are backwards from what (ideally) they should be. this is necessary because UnifyTypes can't backtrack.
                        if (!UnifyTypes(e.E0.Type, e.E1.Type)) {
                            Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type);
                        }
                        if (!UnifyTypes(e.E0.Type, new SetType(new InferredTypeProxy())) &&
                            !UnifyTypes(e.E0.Type, new MultiSetType(new InferredTypeProxy())) &&
                            !UnifyTypes(e.E0.Type, new MapType(new InferredTypeProxy(), new InferredTypeProxy())) &&
                            !UnifyTypes(e.E0.Type, new RegionType())/* Yuyan*/) {
                            Error(expr, "arguments must be of a [multi]set, map type or region type (got {0})", e.E0.Type);
                        }
                        expr.Type = Type.Bool;
                        break;

                    case BinaryExpr.Opcode.Lt:
                    case BinaryExpr.Opcode.Le:
                    case BinaryExpr.Opcode.Add: {
                            if (e.Op == BinaryExpr.Opcode.Lt && e.E0.Type.IsIndDatatype) {
                                if (!UnifyTypes(e.E1.Type, new DatatypeProxy())) {
                                    Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type);
                                }
                                expr.Type = Type.Bool;
                            } else if (e.Op == BinaryExpr.Opcode.Lt && e.E1.Type.IsIndDatatype) {
                                if (!UnifyTypes(e.E0.Type, new DatatypeProxy())) {
                                    Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type);
                                }
                                expr.Type = Type.Bool;
                            } else {
                                bool err = false;
                                if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true))) {
                                    Error(expr, "arguments to {0} must be int or a collection type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
                                    err = true;
                                }
                                if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
                                    Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
                                    err = true;
                                }
                                if (e.Op != BinaryExpr.Opcode.Add) {
                                    expr.Type = Type.Bool;
                                } else if (!err) {
                                    expr.Type = e.E0.Type;
                                }
                            }
                        }
                        break;

                    case BinaryExpr.Opcode.Sub:
                    case BinaryExpr.Opcode.Mul:
                    case BinaryExpr.Opcode.Gt:
                    case BinaryExpr.Opcode.Ge: {
                            if (e.Op == BinaryExpr.Opcode.Gt && e.E0.Type.IsIndDatatype) {
                                if (!UnifyTypes(e.E1.Type, new DatatypeProxy())) {
                                    Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type);
                                }
                                expr.Type = Type.Bool;
                            } else {
                                bool err = false;
                                if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(false))) {
                                    Error(expr, "arguments to {0} must be int or a set (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
                                    err = true;
                                }
                                if (!UnifyTypes(e.E1.Type, e.E0.Type)) {
                                    Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
                                    err = true;
                                }
                                if (e.Op == BinaryExpr.Opcode.Gt || e.Op == BinaryExpr.Opcode.Ge) {
                                    expr.Type = Type.Bool;
                                } else if (!err) {
                                    expr.Type = e.E0.Type;
                                }
                            }
                        }
                        break;

                    case BinaryExpr.Opcode.In:
                    case BinaryExpr.Opcode.NotIn:
                        if (!UnifyTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type))) {
                            Error(expr, "second argument to \"{0}\" must be a set or sequence with elements of type {1}, or a map with domain {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type);
                        }
                        expr.Type = Type.Bool;
                        break;

                    case BinaryExpr.Opcode.Div:
                    case BinaryExpr.Opcode.Mod:
                        if (!UnifyTypes(e.E0.Type, Type.Int)) {
                            Error(expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type);
                        }
                        if (!UnifyTypes(e.E1.Type, Type.Int)) {
                            Error(expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type);
                        }
                        expr.Type = Type.Int;
                        break;

                    default:
                        Contract.Assert(false); throw new cce.UnreachableException();  // unexpected operator
                }
                e.ResolvedOp = ResolveOp(e.Op, e.E1.Type);

            } else if (expr is LetExpr) {
                var e = (LetExpr)expr;
                foreach (var rhs in e.RHSs) {
                    ResolveExpression(rhs, twoState);
                }
                scope.PushMarker();
                if (e.Vars.Count != e.RHSs.Count) {
                    Error(expr, "let expression must have same number of bound variables (found {0}) as RHSs (found {1})", e.Vars.Count, e.RHSs.Count);
                }
                int i = 0;
                foreach (var v in e.Vars) {
                    if (!scope.Push(v.Name, v)) {
                        Error(v, "Duplicate let-variable name: {0}", v.Name);
                    }
                    ResolveType(v.tok, v.Type, null, true);
                    if (i < e.RHSs.Count && !UnifyTypes(v.Type, e.RHSs[i].Type)) {
                        Error(e.RHSs[i].tok, "type of RHS ({0}) does not match type of bound variable ({1})", e.RHSs[i].Type, v.Type);
                    }
                    i++;
                }
                ResolveExpression(e.Body, twoState);
                scope.PopMarker();
                expr.Type = e.Body.Type;

            } else if (expr is NamedExpr) {
                var e = (NamedExpr)expr;
                ResolveExpression(e.Body, twoState);
                if (e.Contract != null) ResolveExpression(e.Contract, twoState);
                e.Type = e.Body.Type;
            } else if (expr is QuantifierExpr) {
                QuantifierExpr e = (QuantifierExpr)expr;
                int prevErrorCount = ErrorCount;
                scope.PushMarker();
                foreach (BoundVar v in e.BoundVars) {
                    if (!scope.Push(v.Name, v)) {
                        Error(v, "Duplicate bound-variable name: {0}", v.Name);
                    }
                    ResolveType(v.tok, v.Type, null, true);
                }
                if (e.Range != null) {
                    ResolveExpression(e.Range, twoState);
                    Contract.Assert(e.Range.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(e.Range.Type, Type.Bool)) {
                        Error(expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type);
                    }
                }
                ResolveExpression(e.Term, twoState);
                Contract.Assert(e.Term.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.Term.Type, Type.Bool)) {
                    Error(expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type);
                }
                // Since the body is more likely to infer the types of the bound variables, resolve it
                // first (above) and only then resolve the attributes (below).
                ResolveAttributes(e.Attributes, twoState);
                scope.PopMarker();
                expr.Type = Type.Bool;

                if (prevErrorCount == ErrorCount) {
                    var missingBounds = new List<BoundVar>();
                    e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.LogicalBody(), e is ExistsExpr, false, missingBounds);
                    if (missingBounds.Count != 0) {
                        // Report errors here about quantifications that depend on the allocation state.
                        var mb = missingBounds;
                        if (currentFunction != null) {
                            mb = new List<BoundVar>();  // (who cares if we allocate another array; this happens only in the case of a resolution error anyhow)
                            foreach (var bv in missingBounds) {
                                if (bv.Type.IsRefType) {
                                    Error(expr, "a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of '{0}'", bv.Name);
                                } else {
                                    mb.Add(bv);
                                }
                            }
                        }
                        if (mb.Count != 0) {
                            e.MissingBounds = mb;
                        }
                    }
                }

            } else if (expr is SetComprehension) {
                var e = (SetComprehension)expr;
                int prevErrorCount = ErrorCount;
                scope.PushMarker();
                foreach (BoundVar v in e.BoundVars) {
                    if (!scope.Push(v.Name, v)) {
                        Error(v, "Duplicate bound-variable name: {0}", v.Name);
                    }
                    ResolveType(v.tok, v.Type, null, true);
                }
                ResolveExpression(e.Range, twoState);
                Contract.Assert(e.Range.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.Range.Type, Type.Bool)) {
                    Error(expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type);
                }
                ResolveExpression(e.Term, twoState);
                Contract.Assert(e.Term.Type != null);  // follows from postcondition of ResolveExpression

                ResolveAttributes(e.Attributes, twoState);
                scope.PopMarker();
                expr.Type = new SetType(e.Term.Type);

                if (prevErrorCount == ErrorCount) {
                    var missingBounds = new List<BoundVar>();
                    e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds);
                    if (missingBounds.Count != 0) {
                        e.MissingBounds = missingBounds;
                        foreach (var bv in e.MissingBounds) {
                            Error(expr, "a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
                        }
                    }
                }

            } else if (expr is MapComprehension) {
                var e = (MapComprehension)expr;
                int prevErrorCount = ErrorCount;
                scope.PushMarker();
                if (e.BoundVars.Count != 1) {
                    Error(e.tok, "a map comprehension must have exactly one bound variable.");
                }
                foreach (BoundVar v in e.BoundVars) {
                    if (!scope.Push(v.Name, v)) {
                        Error(v, "Duplicate bound-variable name: {0}", v.Name);
                    }
                    ResolveType(v.tok, v.Type, null, true);
                }
                ResolveExpression(e.Range, twoState);
                Contract.Assert(e.Range.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.Range.Type, Type.Bool)) {
                    Error(expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type);
                }
                ResolveExpression(e.Term, twoState);
                Contract.Assert(e.Term.Type != null);  // follows from postcondition of ResolveExpression

                ResolveAttributes(e.Attributes, twoState);
                scope.PopMarker();
                expr.Type = new MapType(e.BoundVars[0].Type, e.Term.Type);

                if (prevErrorCount == ErrorCount) {
                    var missingBounds = new List<BoundVar>();
                    e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds);
                    if (missingBounds.Count != 0) {
                        e.MissingBounds = missingBounds;
                        foreach (var bv in e.MissingBounds) {
                            Error(expr, "a map comprehension must produce a finite domain, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
                        }
                    }
                }

            } else if (expr is WildcardExpr) {
                expr.Type = new SetType(new ObjectType());

            } else if (expr is PredicateExpr) {
                var e = (PredicateExpr)expr;
                ResolveExpression(e.Guard, twoState);
                Contract.Assert(e.Guard.Type != null);  // follows from postcondition of ResolveExpression
                ResolveExpression(e.Body, twoState);
                Contract.Assert(e.Body.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.Guard.Type, Type.Bool)) {
                    Error(expr, "guard condition in {0} expression must be a boolean (instead got {1})", e.Kind, e.Guard.Type);
                }
                expr.Type = e.Body.Type;

            } else if (expr is ITEExpr) {
                ITEExpr e = (ITEExpr)expr;
                ResolveExpression(e.Test, twoState);
                Contract.Assert(e.Test.Type != null);  // follows from postcondition of ResolveExpression
                ResolveExpression(e.Thn, twoState);
                Contract.Assert(e.Thn.Type != null);  // follows from postcondition of ResolveExpression
                ResolveExpression(e.Els, twoState);
                Contract.Assert(e.Els.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.Test.Type, Type.Bool)) {
                    Error(expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type);
                }
                if (UnifyTypes(e.Thn.Type, e.Els.Type)) {
                    expr.Type = e.Thn.Type;
                } else {
                    Error(expr, "the two branches of an if-then-else expression must have the same type (got {0} and {1})", e.Thn.Type, e.Els.Type);
                }

            } else if (expr is MatchExpr) {
                MatchExpr me = (MatchExpr)expr;
                Contract.Assert(!twoState);  // currently, match expressions are allowed only at the outermost level of function bodies
                if (matchVarContext == null) {
                    Error(me, "'match' expressions are not supported in this context");
                    matchVarContext = new List<IVariable>();
                }
                ResolveExpression(me.Source, twoState);
                Contract.Assert(me.Source.Type != null);  // follows from postcondition of ResolveExpression
                UserDefinedType sourceType = null;
                DatatypeDecl dtd = null;
                Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
                if (me.Source.Type.IsDatatype) {
                    sourceType = (UserDefinedType)me.Source.Type;
                    dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass);
                }
                Dictionary<string, DatatypeCtor> ctors;
                IVariable goodMatchVariable = null;
                if (dtd == null) {
                    Error(me.Source, "the type of the match source expression must be a datatype (instead found {0})", me.Source.Type);
                    ctors = null;
                } else {
                    Contract.Assert(sourceType != null);  // dtd and sourceType are set together above
                    ctors = datatypeCtors[dtd];
                    Contract.Assert(ctors != null);  // dtd should have been inserted into datatypeCtors during a previous resolution stage

                    IdentifierExpr ie = me.Source.Resolved as IdentifierExpr;
                    if (ie == null || !(ie.Var is Formal || ie.Var is BoundVar)) {
                        Error(me.Source.tok, "match source expression must be a formal parameter of the enclosing function or an enclosing match expression");
                    } else if (!matchVarContext.Contains(ie.Var)) {
                        Error(me.Source.tok, "match source expression '{0}' has already been used as a match source expression in this context", ie.Var.Name);
                    } else {
                        goodMatchVariable = ie.Var;
                    }

                    // build the type-parameter substitution map for this use of the datatype
                    for (int i = 0; i < dtd.TypeArgs.Count; i++) {
                        subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]);
                    }
                }

                Dictionary<string, object> memberNamesUsed = new Dictionary<string, object>();  // this is really a set
                expr.Type = new InferredTypeProxy();
                foreach (MatchCaseExpr mc in me.Cases) {
                    DatatypeCtor ctor = null;
                    if (ctors != null) {
                        Contract.Assert(dtd != null);
                        if (!ctors.TryGetValue(mc.Id, out ctor)) {
                            Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name);
                        } else {
                            Contract.Assert(ctor != null);  // follows from postcondition of TryGetValue
                            mc.Ctor = ctor;
                            if (ctor.Formals.Count != mc.Arguments.Count) {
                                Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count);
                            }
                            if (memberNamesUsed.ContainsKey(mc.Id)) {
                                Error(mc.tok, "member {0} appears in more than one case", mc.Id);
                            } else {
                                memberNamesUsed.Add(mc.Id, null);  // add mc.Id to the set of names used
                            }
                        }
                    }
                    scope.PushMarker();
                    int i = 0;
                    foreach (BoundVar v in mc.Arguments) {
                        if (!scope.Push(v.Name, v)) {
                            Error(v, "Duplicate parameter name: {0}", v.Name);
                        }
                        ResolveType(v.tok, v.Type, null, true);
                        if (ctor != null && i < ctor.Formals.Count) {
                            Formal formal = ctor.Formals[i];
                            Type st = SubstType(formal.Type, subst);
                            if (!UnifyTypes(v.Type, st)) {
                                Error(expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st);
                            }
                            v.IsGhost = formal.IsGhost;
                        }
                        i++;
                    }
                    List<IVariable> innerMatchVarContext = new List<IVariable>(matchVarContext);
                    if (goodMatchVariable != null) {
                        innerMatchVarContext.Remove(goodMatchVariable);  // this variable is no longer available for matching
                    }
                    innerMatchVarContext.AddRange(mc.Arguments);
                    ResolveExpression(mc.Body, twoState, innerMatchVarContext);
                    Contract.Assert(mc.Body.Type != null);  // follows from postcondition of ResolveExpression
                    if (!UnifyTypes(expr.Type, mc.Body.Type)) {
                        Error(mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type);
                    }
                    scope.PopMarker();
                }
                if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
                    // We could complain about the syntactic omission of constructors:
                    //   Error(expr, "match expression does not cover all constructors");
                    // but instead we let the verifier do a semantic check.
                    // So, for now, record the missing constructors:
                    foreach (var ctr in dtd.Ctors) {
                        if (!memberNamesUsed.ContainsKey(ctr.Name)) {
                            me.MissingCases.Add(ctr);
                        }
                    }
                    Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count);
                }
            }else if (expr is BuiltInFunctionExpression){ // Yuyan
                BuiltInFunctionExpression builtInFunExpr = (BuiltInFunctionExpression) expr;

                Contract.Assert(builtInFunExpr.Args != null); // follow the invariant of e

                foreach (Expression arg in builtInFunExpr.Args)
                {
                    ResolveExpression(arg, twoState, matchVarContext);
                }
                ResolveBuiltInFunctionExpression(builtInFunExpr);
                expr.Type = builtInFunExpr.Type;

            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected expression
            }

            if (expr.Type == null) {
                // some resolution error occurred
                expr.Type = Type.Flexible;
            }
        }

        void ResolveBuiltInFunctionExpression(BuiltInFunctionExpression e)
        {
            Contract.Requires(e != null);

            Contract.Assert(e.FunctionName != null && e.Args != null); // follow the invariant of e

            if (e.FunctionName == "IsRecursiveType")
            {
                if (e.Args.Count != 1)
                {
                    Error(e.tok, "Wrong numbers of function {0} arguments, expected: 1, actural:{1}", e.FunctionName, e.Args.Count);
                }
                else
                {
                    if (!(e.Args[0].Resolved.Type is UserDefinedType))
                    {
                        Error(e.tok, "UserDeinfedType expected, got {0}", e.Args[0].Resolved.Type.ToString());
                    }
                    else
                    {
                        e.Type = Type.Bool;
                    }
                }
            }
            else
            {
                Error(e.tok, "undefined function {0}", e.FunctionName);
            }

            if (e.Type == null)
            {
                // some resolution error occurred
                e.Type = Type.Flexible;
            }
        }

        void ResolvePatternExpression(Expression e) {
            Contract.Requires(e != null);

            if (e is BinaryExpr) {
                BinaryExpr be = (BinaryExpr)e;
                if (be.E0 is PatternExpression) {
                    ((PatternExpression)be.E0).Type = be.E1.Type;
                } else if (be.E1 is PatternExpression) {
                    ((PatternExpression)be.E1).Type = be.E0.Type;
                }
            }
        }

        // -1: unresovled
        // 0: no filter
        // 1: filter by instance
        // 2: filter by instance and field name
        // 3: filter by classname
        // 4: filter by classname and field name
        int ResolveFilterType(List<Expression> filters) {
            Contract.Requires(filters != null);
            int type = -1;
            if (filters.Count == 0) {
                type = 0;
            } else {
                Contract.Assert(filters.Count > 0);
                if (filters[0] is ClassExpression) {
                    ClassExpression ce = (ClassExpression)filters[0];
                    if (filters.Count == 1) {
                        type = 3;
                    } else {
                        Contract.Assert(filters.Count > 1);
                        for (int i = 1; i < filters.Count; i++) {
                            if (filters[i] is FieldExpression) {
                                FieldExpression fe = (FieldExpression)filters[i];
                                ClassDecl cd = ((ClassExpression)filters[0]).ClsDecl;
                                foreach (MemberDecl md in cd.Members) {
                                    if (md is Field) {
                                        Field f = (Field)md;
                                        if (f.Name == fe.FieldName) {
                                            fe.Field = f;
                                            break;
                                        }
                                    }
                                }
                                if (fe.Field == null) {
                                    Error(fe.tok, "member {0} is in the class {1} does not refer to a field.", fe.FieldName, cd.Name);
                                }
                            } else {
                                Error(filters[i], "FieldExpression expected, instead of {0}.", filters[i]);
                            }
                        }

                        type = 4;
                    }
                } else { // an object instance
                    if (filters.Count == 1) {
                        type = 1;
                    } else {
                        Contract.Assert(filters.Count > 1);
                        for (int i = 1; i < filters.Count; i++) {
                            if (filters[i] is FieldExpression) {
                                FieldExpression fe = (FieldExpression)filters[i];
                                NonProxyType nptype;
                                MemberDecl member = ResolveMember(filters[0].tok, filters[0].Type, fe.tok.val, out nptype);
                                UserDefinedType ctype = (UserDefinedType)nptype;  // correctness of cast follows from the DenotesClass test above
                                if (member == null) {
                                    // Yuyan: a wildcard expression is not allowed here. An error should report.
                                    // otherwise error has already been reported by ResolveMember
                                    Error(filters[i], "a wildcard expression is not allowed in a region filter expression");
                                } else if (!(member is Field)) {
                                    Error(filters[i], "member {0} in type {1} does not refer to a field", fe.tok.val, filters[0].Type);
                                } else {
                                    Contract.Assert(ctype != null && ctype.ResolvedClass != null);  // follows from postcondition of ResolveMember
                                    fe.Field = (Field)member;
                                }
                            }
                        }
                        type = 2;
                    }
                }
            }
            return type;
        }

        private void ResolveDatatypeValue(bool twoState, DatatypeValue dtv, DatatypeDecl dt) {
            // this resolution is a little special, in that the syntax shows only the base name, not its instantiation (which is inferred)
            List<Type> gt = new List<Type>(dt.TypeArgs.Count);
            Dictionary<TypeParameter, Type> subst = new Dictionary<TypeParameter, Type>();
            for (int i = 0; i < dt.TypeArgs.Count; i++) {
                Type t = new InferredTypeProxy();
                gt.Add(t);
                dtv.InferredTypeArgs.Add(t);
                subst.Add(dt.TypeArgs[i], t);
            }
            // Construct a resolved type directly, as we know the declaration is dt.
            dtv.Type = new UserDefinedType(dtv.tok, dtv.DatatypeName, dt, gt);

            DatatypeCtor ctor;
            if (!datatypeCtors[dt].TryGetValue(dtv.MemberName, out ctor)) {
                Error(dtv.tok, "undeclared constructor {0} in datatype {1}", dtv.MemberName, dtv.DatatypeName);
            } else {
                Contract.Assert(ctor != null);  // follows from postcondition of TryGetValue
                dtv.Ctor = ctor;
                if (ctor.Formals.Count != dtv.Arguments.Count) {
                    Error(dtv.tok, "wrong number of arguments to datatype constructor {0} (found {1}, expected {2})", dtv.DatatypeName, dtv.Arguments.Count, ctor.Formals.Count);
                }
            }
            int j = 0;
            foreach (Expression arg in dtv.Arguments) {
                Formal formal = ctor != null && j < ctor.Formals.Count ? ctor.Formals[j] : null;
                ResolveExpression(arg, twoState, null);
                Contract.Assert(arg.Type != null);  // follows from postcondition of ResolveExpression
                if (arg is PatternExpression) {   // Yuyan: resove pattern expression here
                    PatternExpression pe = (PatternExpression)arg;
                    pe.Type = formal.Type;
                    ResolveType(pe.tok, pe.Type, dt.TypeArgs, true);
                    ResolveExpression(pe, twoState);
                }

                if (formal != null) {
                    Type st = SubstType(formal.Type, subst);
                    if (!UnifyTypes(arg.Type, st)) {
                        Error(arg.tok, "incorrect type of datatype constructor argument (found {0}, expected {1})", arg.Type, st);
                    }
                }
                j++;
            }
        }

        private bool ComparableTypes(Type A, Type B) {
            if (A.IsArrayType && B.IsArrayType) {
                Type a = UserDefinedType.ArrayElementType(A);
                Type b = UserDefinedType.ArrayElementType(B);
                return CouldPossiblyBeSameType(a, b);
            } else
                if (A is UserDefinedType && B is UserDefinedType) {
                    UserDefinedType a = (UserDefinedType)A;
                    UserDefinedType b = (UserDefinedType)B;
                    if (a.ResolvedClass != null && b.ResolvedClass != null && a.ResolvedClass.Name == b.ResolvedClass.Name) {
                        if (a.TypeArgs.Count != b.TypeArgs.Count) {
                            return false; // this probably doesn't happen if the classes are the same.
                        }
                        for (int i = 0; i < a.TypeArgs.Count; i++) {
                            if (!CouldPossiblyBeSameType(a.TypeArgs[i], b.TypeArgs[i]))
                                return false;
                        }
                        // all parameters could be the same
                        return true;
                    }
                    // either we don't know what class it is yet, or the classes mismatch
                    return false;
                }
            return false;
        }
        private bool CouldPossiblyBeSameType(Type A, Type B) {
            if (A.IsTypeParameter || B.IsTypeParameter) {
                return true;
            }
            if (A.IsArrayType && B.IsArrayType) {
                Type a = UserDefinedType.ArrayElementType(A);
                Type b = UserDefinedType.ArrayElementType(B);
                return CouldPossiblyBeSameType(a, b);
            }
            if (A is UserDefinedType && B is UserDefinedType) {
                UserDefinedType a = (UserDefinedType)A;
                UserDefinedType b = (UserDefinedType)B;
                if (a.ResolvedClass != null && b.ResolvedClass != null && a.ResolvedClass == b.ResolvedClass) {
                    if (a.TypeArgs.Count != b.TypeArgs.Count) {
                        return false; // this probably doesn't happen if the classes are the same.
                    }
                    for (int i = 0; i < a.TypeArgs.Count; i++) {
                        if (!CouldPossiblyBeSameType(a.TypeArgs[i], b.TypeArgs[i]))
                            return false;
                    }
                    // all parameters could be the same
                    return true;
                }
                // either we don't know what class it is yet, or the classes mismatch
                return false;
            }
            return false;
        }

        /// <summary>
        /// Generate an error for every non-ghost feature used in "expr".
        /// Requires "expr" to have been successfully resolved.
        /// </summary>
        void CheckIsNonGhost(Expression expr) {
            Contract.Requires(expr != null);
            Contract.Requires(expr.WasResolved());  // this check approximates the requirement that "expr" be resolved

            if (expr is IdentifierExpr) {
                var e = (IdentifierExpr)expr;
                if (e.Var != null && e.Var.IsGhost) {
                    Error(expr, "ghost variables are allowed only in specification contexts");
                    return;
                }

            } else if (expr is FieldSelectExpr) {
                var e = (FieldSelectExpr)expr;
                if (e.Field != null && e.Field.IsGhost) {
                    Error(expr, "ghost fields are allowed only in specification contexts");
                    return;
                }

            } else if (expr is FunctionCallExpr) {
                var e = (FunctionCallExpr)expr;
                if (e.Function != null) {
                    if (e.Function.IsGhost) {
                        Error(expr, "function calls are allowed only in specification contexts (consider declaring the function a 'function method')");
                        return;
                    }
                    // function is okay, so check all NON-ghost arguments
                    CheckIsNonGhost(e.Receiver);
                    for (int i = 0; i < e.Function.Formals.Count; i++) {
                        if (!e.Function.Formals[i].IsGhost) {
                            CheckIsNonGhost(e.Args[i]);
                        }
                    }
                }
                return;

            } else if (expr is DatatypeValue) {
                var e = (DatatypeValue)expr;
                // check all NON-ghost arguments
                // note that if resolution is successful, then |e.Arguments| == |e.Ctor.Formals|
                for (int i = 0; i < e.Arguments.Count; i++) {
                    if (!e.Ctor.Formals[i].IsGhost) {
                        CheckIsNonGhost(e.Arguments[i]);
                    }
                }
                return;

            } else if (expr is OldExpr) {
                Error(expr, "old expressions are allowed only in specification and ghost contexts");
                return;

            } else if (expr is FreshExpr) {
                Error(expr, "fresh expressions are allowed only in specification and ghost contexts");
                return;

            } else if (expr is PredicateExpr) {
                var e = (PredicateExpr)expr;
                // ignore the guard
                CheckIsNonGhost(e.Body);
                return;

            } else if (expr is BinaryExpr) {
                var e = (BinaryExpr)expr;
                switch (e.ResolvedOp) {
                    case BinaryExpr.ResolvedOpcode.RankGt:
                    case BinaryExpr.ResolvedOpcode.RankLt:
                        Error(expr, "rank comparisons are allowed only in specification and ghost contexts");
                        return;
                    default:
                        break;
                }

            } else if (expr is QuantifierExpr) {
                var e = (QuantifierExpr)expr;
                if (e.MissingBounds != null) {
                    foreach (var bv in e.MissingBounds) {
                        Error(expr, "quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name);
                    }
                    return;
                }
            } else if (expr is NamedExpr) {
                if (!moduleInfo.IsGhost)
                    CheckIsNonGhost(((NamedExpr)expr).Body);
                return;
            }

            foreach (var ee in expr.SubExpressions) {
                CheckIsNonGhost(ee);
            }
        }

        /// <summary>
        /// If "!allowMethodCall" or if what is being called does not refer to a method, resolves "e" and returns "null".
        /// Otherwise (that is, if "allowMethodCall" and what is being called refers to a method), resolves the receiver
        /// of "e" but NOT the arguments, and returns a CallRhs corresponding to the call.
        /// </summary>
        CallRhs ResolveFunctionCallExpr(FunctionCallExpr e, bool twoState, bool allowMethodCall) {
            ResolveReceiver(e.Receiver, twoState);
            Contract.Assert(e.Receiver.Type != null);  // follows from postcondition of ResolveExpression
            NonProxyType nptype;
            MemberDecl member = ResolveMember(e.tok, e.Receiver.Type, e.Name, out nptype);
#if !NO_WORK_TO_BE_DONE
            UserDefinedType ctype = (UserDefinedType)nptype;
#endif
            if (member == null) {
                // Yuyan
                // error has already been reported by ResolveMember
                // otherwise it is a wildcard expression, which is not allowed here. An error should report
                Error(e.tok, "Wildcard expression is not allowed in a function call expression");
            } else if (member is Method) {
                if (allowMethodCall) {
                    // it's a method
                    return new CallRhs(e.tok, e.Receiver, e.Name, e.Args);
                } else {
                    Error(e, "member {0} in type {1} refers to a method, but only functions can be used in this context", e.Name, cce.NonNull(ctype).Name);
                }
            } else if (!(member is Function)) {
                Error(e, "member {0} in type {1} does not refer to a function", e.Name, cce.NonNull(ctype).Name);
            } else {
                Function function = (Function)member;
                e.Function = function;
                if (function is CoPredicate) {
                    ((CoPredicate)function).Uses.Add(e);
                }
                if (e.Receiver is StaticReceiverExpr && !function.IsStatic) {
                    Error(e, "an instance function must be selected via an object, not just a class name");
                }
                if (function.Formals.Count != e.Args.Count) {
                    Error(e, "wrong number of function arguments (got {0}, expected {1})", e.Args.Count, function.Formals.Count);
                } else {
                    Contract.Assert(ctype != null);  // follows from postcondition of ResolveMember
                    if (!function.IsStatic) {
                        if (!scope.AllowInstance && e.Receiver is ThisExpr) {
                            // The call really needs an instance, but that instance is given as 'this', which is not
                            // available in this context.  In most cases, occurrences of 'this' inside e.Receiver would
                            // have been caught in the recursive call to resolve e.Receiver, but not the specific case
                            // of e.Receiver being 'this' (explicitly or implicitly), for that case needs to be allowed
                            // in the event that a static function calls another static function (and note that we need the
                            // type of the receiver in order to find the method, so we could not have made this check
                            // earlier).
                            Error(e.Receiver, "'this' is not allowed in a 'static' context");
                        } else if (e.Receiver is StaticReceiverExpr) {
                            Error(e.Receiver, "call to instance function requires an instance");
                        }
                    }
                    // build the type substitution map
                    e.TypeArgumentSubstitutions = new Dictionary<TypeParameter, Type>();
                    for (int i = 0; i < ctype.TypeArgs.Count; i++) {
                        e.TypeArgumentSubstitutions.Add(cce.NonNull(ctype.ResolvedClass).TypeArgs[i], ctype.TypeArgs[i]);
                    }
                    foreach (TypeParameter p in function.TypeArgs) {
                        e.TypeArgumentSubstitutions.Add(p, new ParamTypeProxy(p));
                    }
                    // type check the arguments
                    for (int i = 0; i < function.Formals.Count; i++) {
                        Type s = SubstType(function.Formals[i].Type, e.TypeArgumentSubstitutions);

                        Expression farg = e.Args[i];
                        if (farg is PatternExpression) {
                            PatternExpression pe = (PatternExpression)farg;
                            pe.E.Type = s;
                        }
                        ResolveExpression(farg, twoState);
                        Contract.Assert(farg.Type != null);  // follows from postcondition of ResolveExpression
                        
                        if (!UnifyTypes(farg.Type, s)) {
                            Error(e, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type);
                        }
                    }
                    e.Type = SubstType(function.ResultType, e.TypeArgumentSubstitutions);
                }

                // Resolution termination check
                if (currentFunction != null && currentFunction.EnclosingClass != null && function.EnclosingClass != null) {
                    ModuleDefinition callerModule = currentFunction.EnclosingClass.Module;
                    ModuleDefinition calleeModule = function.EnclosingClass.Module;
                    if (callerModule == calleeModule) {
                        // intra-module call; this is allowed; add edge in module's call graph
                        callerModule.CallGraph.AddEdge(currentFunction, function);
                        if (currentFunction == function) {
                            currentFunction.IsRecursive = true;  // self recursion (mutual recursion is determined elsewhere)
                        }
                    } else {
                        //Contract.Assert(dependencies.Reaches(callerModule, calleeModule));
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// If "!allowMethodCall", or if "e" does not designate a method call, resolves "e" and returns "null".
        /// Otherwise, resolves all sub-parts of "e" and returns a (resolved) CallRhs expression representing the call.
        /// </summary>
        CallRhs ResolveIdentifierSequence(IdentifierSequence e, bool twoState, bool allowMethodCall) {
            // Look up "id" as follows:
            //  - local variable, parameter, or bound variable (if this clashes with something of interest, one can always rename the local variable locally)
            //  - unamibugous type/module name (class, datatype, sub-module (including submodules of imports) or arbitrary-type)
            //       (if two imported types have the same name, an error message is produced here)
            //  - unambiguous constructor name of a datatype (if two constructors have the same name, an error message is produced here)
            //  - field, function or method name (with implicit receiver) (if the field is occluded by anything above, one can use an explicit "this.")
            //  - iterator
            //  - static function or method in the enclosing module, or its imports.

            Expression r = null;  // resolved version of e
            CallRhs call = null;

            TopLevelDecl decl;
            Tuple<DatatypeCtor, bool> pair;
            Dictionary<string, MemberDecl> members;
            MemberDecl member;
            var id = e.Tokens[0];
            if (scope.Find(id.val) != null) {
                // ----- root is a local variable, parameter, or bound variable
                r = new IdentifierExpr(id, id.val);
                ResolveExpression(r, twoState);
                r = ResolveSuffix(r, e, 1, twoState, allowMethodCall, out call);

            } else if (moduleInfo.TopLevels.TryGetValue(id.val, out decl)) {
                if (decl is AmbiguousTopLevelDecl) {
                    Error(id, "The name {0} ambiguously refers to a type in one of the modules {1}", id.val, ((AmbiguousTopLevelDecl)decl).ModuleNames());
                } else if (e.Tokens.Count == 1 && e.Arguments == null) {
                    Error(id, "name of type ('{0}') is used as a variable", id.val);
                } else if (e.Tokens.Count == 1 && e.Arguments != null) {
                    Error(id, "name of type ('{0}') is used as a function", id.val);
                    // resolve the arguments nonetheless
                    foreach (var arg in e.Arguments) {
                        ResolveExpression(arg, twoState);
                    }
                } else if (decl is ClassDecl) {
                    // ----- root is a class
                    var cd = (ClassDecl)decl;
                    r = ResolveSuffix(new StaticReceiverExpr(id, cd), e, 1, twoState, allowMethodCall, out call);

                } else if (decl is ModuleDecl) {
                    // ----- root is a submodule
                    if (!(1 < e.Tokens.Count)) {
                        Error(e.tok, "module {0} cannot be used here", ((ModuleDecl)decl).Name);
                    }
                    call = ResolveIdentifierSequenceModuleScope(e, 1, ((ModuleDecl)decl).Signature, twoState, allowMethodCall);
                } else {
                    // ----- root is a datatype
                    var dt = (DatatypeDecl)decl;  // otherwise, unexpected TopLevelDecl
                    var args = (e.Tokens.Count == 2 ? e.Arguments : null) ?? new List<Expression>();
                    r = new DatatypeValue(id, id.val, e.Tokens[1].val, args);
                    ResolveExpression(r, twoState);
                    if (e.Tokens.Count != 2) {
                        r = ResolveSuffix(r, e, 2, twoState, allowMethodCall, out call);
                    }
                }

            } else if (moduleInfo.Ctors.TryGetValue(id.val, out pair)) {
                // ----- root is a datatype constructor
                if (pair.Item2) {
                    // there is more than one constructor with this name
                    Error(id, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", id.val, pair.Item1.EnclosingDatatype.Name);
                } else {
                    var args = (e.Tokens.Count == 1 ? e.Arguments : null) ?? new List<Expression>();
                    r = new DatatypeValue(id, pair.Item1.EnclosingDatatype.Name, id.val, args);
                    ResolveExpression(r, twoState);
                    if (e.Tokens.Count != 1) {
                        r = ResolveSuffix(r, e, 1, twoState, allowMethodCall, out call);
                    }
                }

            } else if ((currentClass != null && classMembers.TryGetValue(currentClass, out members) && members.TryGetValue(id.val, out member))
                       || moduleInfo.StaticMembers.TryGetValue(id.val, out member)) // try static members of the current module too.
      {
                // ----- field, function, or method
                if (member is AmbiguousMemberDecl) {
                    Contract.Assert(member.IsStatic); // currently, static members of _default are the only thing which can be ambiguous.
                    Error(id, "The name {0} ambiguously refers to a static member in one of the modules {1}", id.val, ((AmbiguousMemberDecl)member).ModuleNames());
                } else {
                    Expression receiver;
                    if (member.IsStatic) {
                        receiver = new StaticReceiverExpr(id, (ClassDecl)member.EnclosingClass);
                    } else {
                        if (!scope.AllowInstance) {
                            Error(id, "'this' is not allowed in a 'static' context");
                            // nevertheless, set "receiver" to a value so we can continue resolution
                        }
                        receiver = new ImplicitThisExpr(id);
                        receiver.Type = GetThisType(id, (ClassDecl)member.EnclosingClass);  // resolve here
                    }
                    r = ResolveSuffix(receiver, e, 0, twoState, allowMethodCall, out call);
                }

            } else {
                Error(id, "unresolved identifier: {0}", id.val);
                // resolve arguments, if any
                if (e.Arguments != null) {
                    foreach (var arg in e.Arguments) {
                        ResolveExpression(arg, twoState);
                    }
                }
            }

            if (r != null) {
                e.ResolvedExpression = r;
                e.Type = r.Type;
            }
            return call;
        }

        CallRhs ResolveIdentifierSequenceModuleScope(IdentifierSequence e, int p, ModuleSignature sig, bool twoState, bool allowMethodCall) {
            // Look up "id" as follows:
            //  - unamibugous type/module name (class, datatype, sub-module (including submodules of imports) or arbitrary-type)
            //       (if two imported types have the same name, an error message is produced here)
            //  - static function or method of sig.
            // This is used to look up names that appear after a dot in a sequence identifier. For example, in X.M.*, M should be looked up in X, but
            // should not consult the local scope. This distingushes this from the above, in that local scope, imported modules, etc. are ignored.
            Contract.Requires(p < e.Tokens.Count);
            Expression r = null;  // resolved version of e
            CallRhs call = null;

            TopLevelDecl decl;
            MemberDecl member;
            Tuple<DatatypeCtor, bool> pair;
            var id = e.Tokens[p];
            sig = GetSignature(sig);
            if (sig.TopLevels.TryGetValue(id.val, out decl)) {
                if (decl is AmbiguousTopLevelDecl) {
                    Error(id, "The name {0} ambiguously refers to a something in one of the modules {1}", id.val, ((AmbiguousTopLevelDecl)decl).ModuleNames());
                } else if (decl is ClassDecl) {
                    // ----- root is a class
                    var cd = (ClassDecl)decl;
                    r = ResolveSuffix(new StaticReceiverExpr(id, cd), e, p + 1, twoState, allowMethodCall, out call);

                } else if (decl is ModuleDecl) {
                    // ----- root is a submodule
                    if (!(p + 1 < e.Tokens.Count)) {
                        Error(e.tok, "module {0} cannot be used here", ((ModuleDecl)decl).Name);
                    }
                    call = ResolveIdentifierSequenceModuleScope(e, p + 1, ((ModuleDecl)decl).Signature, twoState, allowMethodCall);
                } else {
                    // ----- root is a datatype
                    var dt = (DatatypeDecl)decl;  // otherwise, unexpected TopLevelDecl
                    var args = (e.Tokens.Count == p + 2 ? e.Arguments : null) ?? new List<Expression>();
                    r = new DatatypeValue(id, id.val, e.Tokens[p + 1].val, args);
                    ResolveDatatypeValue(twoState, (DatatypeValue)r, dt);
                    if (e.Tokens.Count != p + 2) {
                        r = ResolveSuffix(r, e, p + 2, twoState, allowMethodCall, out call);
                    }
                }
            } else if (sig.Ctors.TryGetValue(id.val, out pair)) {
                // ----- root is a datatype constructor
                if (pair.Item2) {
                    // there is more than one constructor with this name
                    Error(id, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", id.val, pair.Item1.EnclosingDatatype.Name);
                } else {
                    var dt = pair.Item1.EnclosingDatatype;
                    var args = (e.Tokens.Count == p + 1 ? e.Arguments : null) ?? new List<Expression>();
                    r = new DatatypeValue(id, dt.Name, id.val, args);
                    ResolveDatatypeValue(twoState, (DatatypeValue)r, dt);
                    if (e.Tokens.Count != p + 1) {
                        r = ResolveSuffix(r, e, p + 1, twoState, allowMethodCall, out call);
                    }
                }
            } else if (sig.StaticMembers.TryGetValue(id.val, out member)) // try static members of the current module too.
      {
                // ----- function, or method
                Expression receiver;
                Contract.Assert(member.IsStatic);
                receiver = new StaticReceiverExpr(id, (ClassDecl)member.EnclosingClass);
                r = ResolveSuffix(receiver, e, p, twoState, allowMethodCall, out call);

            } else {
                Error(id, "unresolved identifier: {0}", id.val);
                // resolve arguments, if any
                if (e.Arguments != null) {
                    foreach (var arg in e.Arguments) {
                        ResolveExpression(arg, twoState);
                    }
                }
            }

            if (r != null) {
                e.ResolvedExpression = r;
                e.Type = r.Type;
            }
            return call;
        }

        private ModuleSignature GetSignature(ModuleSignature sig) {
            if (useCompileSignatures) {
                while (sig.CompileSignature != null)
                    sig = sig.CompileSignature;
            }
            return sig;
        }
        /// <summary>
        /// Given resolved expression "r" and unresolved expressions e.Tokens[p..] and e.Arguments.
        /// Returns a resolved version of the expression:
        ///   r    . e.Tokens[p]    . e.Tokens[p+1]    ...    . e.Tokens[e.Tokens.Count-1]    ( e.Arguments )
        /// Except, if "allowMethodCall" is "true" and the would-be-returned value designates a method
        /// call, instead returns null and returns "call" as a non-null value.
        /// </summary>
        Expression ResolveSuffix(Expression r, IdentifierSequence e, int p, bool twoState, bool allowMethodCall, out CallRhs call) {
            Contract.Requires(r != null);
            Contract.Requires(e != null);
            Contract.Requires(0 <= p && p <= e.Tokens.Count);
            Contract.Ensures((Contract.Result<Expression>() != null && Contract.ValueAtReturn(out call) == null) ||
              (allowMethodCall && Contract.Result<Expression>() == null && Contract.ValueAtReturn(out call) != null));

            call = null;
            int nonCallArguments = e.Arguments == null ? e.Tokens.Count : e.Tokens.Count - 1;
            for (; p < nonCallArguments; p++) {
                var resolved = ResolvePredicateOrField(e.Tokens[p], r, e.Tokens[p].val);
                if (resolved != null) {
                    r = resolved;
                    ResolveExpression(r, twoState, null);
                }
            }

            if (p < e.Tokens.Count) {
                Contract.Assert(e.Arguments != null);


                bool itIsAMethod = false;
                if (allowMethodCall) {
                    var udt = r.Type.Normalize() as UserDefinedType;
                    if (udt != null && udt.ResolvedClass != null) {
                        Dictionary<string, MemberDecl> members;
                        if (udt.ResolvedClass is ClassDecl) {
                            classMembers.TryGetValue((ClassDecl)udt.ResolvedClass, out members);
                        } else {
                            members = null;
                        }
                        MemberDecl member;
                        if (members != null && members.TryGetValue(e.Tokens[p].val, out member) && member is Method) {
                            itIsAMethod = true;
                        }
                    }
                }
                if (itIsAMethod) {
                    // method
                    call = new CallRhs(e.Tokens[p], r, e.Tokens[p].val, e.Arguments);
                    r = null;
                } else {
                    r = new FunctionCallExpr(e.Tokens[p], e.Tokens[p].val, r, e.OpenParen, e.Arguments);
                    ResolveExpression(r, twoState, null);
                }
            } else if (e.Arguments != null) {
                Contract.Assert(p == e.Tokens.Count);
                Error(e.OpenParen, "non-function expression is called with parameters");
                // resolve the arguments nonetheless
                foreach (var arg in e.Arguments) {
                    ResolveExpression(arg, twoState);
                }
            }
            return r;
        }

        /// <summary>
        /// Resolves "obj . suffixName" to either a parameter-less predicate invocation or a field selection.
        /// Expects "obj" already to have been resolved.
        /// On success, returns the result of the resolution--as an un-resolved expression.
        /// On failure, returns null (in which case an error has been reported to the user).
        /// </summary>
        Expression/*?*/ ResolvePredicateOrField(IToken tok, Expression obj, string suffixName) {
            Contract.Requires(tok != null);
            Contract.Requires(obj != null);
            Contract.Requires(obj.Type != null);  // obj is expected already to have been resolved
            Contract.Requires(suffixName != null);

            NonProxyType nptype;
            MemberDecl member = ResolveMember(tok, obj.Type, suffixName, out nptype);
            if (member == null) {
                // Yuyan
                // // error has already been reported by ResolveMember if it is a valid filedname
                // otherwise it is a wildcard expression, feed * 
                return new FieldSelectExpr(tok, obj, suffixName); // suffixName must equal to "="
            } else if (member is Predicate && ((Predicate)member).Formals.Count == 0) {
                // parameter-less predicates are allowed to be used without parentheses
                return new FunctionCallExpr(tok, suffixName, obj, null, new List<Expression>());
            } else {
                // assume it's a field and let the resolution of the FieldSelectExpr check any further problems
                return new FieldSelectExpr(tok, obj, suffixName);
            }
        }

        /// <summary>
        /// Tries to find a bounded pool for each of the bound variables "bvars" of "expr".  If this process
        /// fails, then "null" is returned and the bound variables for which the process fails are added to "missingBounds".
        /// If "returnAllBounds" is false, then:
        ///   -- at most one BoundedPool per variable is returned
        ///   -- every IntBoundedPool returned has both a lower and an upper bound
        ///   -- no SuperSetBoundedPool is returned
        /// If "returnAllBounds" is true, then:
        ///   -- a variable may give rise to several BoundedPool's
        ///   -- IntBoundedPool bounds may have just one component
        ///   -- a non-null return value means that some bound were found for each variable (but, for example, perhaps one
        ///      variable only has lower bounds, no upper bounds)
        /// Requires "expr" to be successfully resolved.
        /// </summary>
        public static List<ComprehensionExpr.BoundedPool> DiscoverBounds(IToken tok, List<BoundVar> bvars, Expression expr, bool polarity, bool returnAllBounds, List<BoundVar> missingBounds) {
            Contract.Requires(tok != null);
            Contract.Requires(bvars != null);
            Contract.Requires(missingBounds != null);
            Contract.Requires(expr != null);
            Contract.Requires(expr.Type != null);  // a sanity check (but not a complete proof) that "expr" has been resolved
            Contract.Ensures(
              (returnAllBounds && Contract.OldValue(missingBounds.Count) <= missingBounds.Count) ||
              (!returnAllBounds &&
               Contract.Result<List<ComprehensionExpr.BoundedPool>>() != null &&
               Contract.Result<List<ComprehensionExpr.BoundedPool>>().Count == bvars.Count &&
               Contract.OldValue(missingBounds.Count) == missingBounds.Count) ||
              (!returnAllBounds &&
               Contract.Result<List<ComprehensionExpr.BoundedPool>>() == null &&
               Contract.OldValue(missingBounds.Count) < missingBounds.Count));

            var bounds = new List<ComprehensionExpr.BoundedPool>();
            bool foundError = false;
            for (int j = 0; j < bvars.Count; j++) {
                var bv = bvars[j];
                if (bv.Type is BoolType) {
                    // easy
                    bounds.Add(new ComprehensionExpr.BoolBoundedPool());
                } else if (bv.Type.IsIndDatatype && (bv.Type.AsIndDatatype).HasFinitePossibleValues) {
                    bounds.Add(new ComprehensionExpr.DatatypeBoundedPool(bv.Type.AsIndDatatype));
                } else {
                    // Go through the conjuncts of the range expression to look for bounds.
                    Expression lowerBound = bv.Type is NatType ? new LiteralExpr(bv.tok, new BigInteger(0)) : null;
                    Expression upperBound = null;
                    bool foundBoundsForBv = false;
                    if (returnAllBounds && lowerBound != null) {
                        bounds.Add(new ComprehensionExpr.IntBoundedPool(lowerBound, upperBound));
                        lowerBound = null;
                        foundBoundsForBv = true;
                    }
                    foreach (var conjunct in NormalizedConjuncts(expr, polarity)) {
                        var c = conjunct as BinaryExpr;
                        if (c == null) {
                            goto CHECK_NEXT_CONJUNCT;
                        }
                        var e0 = c.E0;
                        var e1 = c.E1;
                        int whereIsBv = SanitizeForBoundDiscovery(bvars, j, c.ResolvedOp, ref e0, ref e1);
                        if (whereIsBv < 0) {
                            goto CHECK_NEXT_CONJUNCT;
                        }
                        switch (c.ResolvedOp) {
                            case BinaryExpr.ResolvedOpcode.InSet:
                                if (whereIsBv == 0) {
                                    bounds.Add(new ComprehensionExpr.SetBoundedPool(e1));
                                    foundBoundsForBv = true;
                                    if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
                                }
                                break;
                            case BinaryExpr.ResolvedOpcode.Subset:
                                if (returnAllBounds && whereIsBv == 1) {
                                    bounds.Add(new ComprehensionExpr.SuperSetBoundedPool(e0));
                                    foundBoundsForBv = true;
                                }
                                break;
                            case BinaryExpr.ResolvedOpcode.InMultiSet:
                                if (whereIsBv == 0) {
                                    bounds.Add(new ComprehensionExpr.SetBoundedPool(e1));
                                    foundBoundsForBv = true;
                                    if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
                                }
                                break;
                            case BinaryExpr.ResolvedOpcode.InSeq:
                                if (whereIsBv == 0) {
                                    bounds.Add(new ComprehensionExpr.SeqBoundedPool(e1));
                                    foundBoundsForBv = true;
                                    if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
                                }
                                break;
                            case BinaryExpr.ResolvedOpcode.InMap:
                                if (whereIsBv == 0) {
                                    bounds.Add(new ComprehensionExpr.SetBoundedPool(e1));
                                    foundBoundsForBv = true;
                                    if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
                                }
                                break;
                            case BinaryExpr.ResolvedOpcode.EqCommon:
                                if (bv.Type is IntType) {
                                    var otherOperand = whereIsBv == 0 ? e1 : e0;
                                    bounds.Add(new ComprehensionExpr.IntBoundedPool(otherOperand, Plus(otherOperand, 1)));
                                    foundBoundsForBv = true;
                                    if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
                                } else if (returnAllBounds && bv.Type is SetType) {
                                    var otherOperand = whereIsBv == 0 ? e1 : e0;
                                    bounds.Add(new ComprehensionExpr.SuperSetBoundedPool(otherOperand));
                                    foundBoundsForBv = true;
                                }
                                break;
                            case BinaryExpr.ResolvedOpcode.Gt:
                            case BinaryExpr.ResolvedOpcode.Ge:
                                Contract.Assert(false); throw new cce.UnreachableException();  // promised by postconditions of NormalizedConjunct
                            case BinaryExpr.ResolvedOpcode.Lt:
                                if (whereIsBv == 0 && upperBound == null) {
                                    upperBound = e1;  // bv < E
                                } else if (whereIsBv == 1 && lowerBound == null) {
                                    lowerBound = Plus(e0, 1);  // E < bv
                                }
                                break;
                            case BinaryExpr.ResolvedOpcode.Le:
                                if (whereIsBv == 0 && upperBound == null) {
                                    upperBound = Plus(e1, 1);  // bv <= E
                                } else if (whereIsBv == 1 && lowerBound == null) {
                                    lowerBound = e0;  // E <= bv
                                }
                                break;
                            default:
                                break;
                        }
                        if ((lowerBound != null && upperBound != null) ||
                            (returnAllBounds && (lowerBound != null || upperBound != null))) {
                            // we have found two halves (or, in the case of returnAllBounds, we have found some bound)
                            bounds.Add(new ComprehensionExpr.IntBoundedPool(lowerBound, upperBound));
                            lowerBound = null;
                            upperBound = null;
                            foundBoundsForBv = true;
                            if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE;
                        }
                    CHECK_NEXT_CONJUNCT: ;
                    }
                    if (!foundBoundsForBv) {
                        // we have checked every conjunct in the range expression and still have not discovered good bounds
                        missingBounds.Add(bv);  // record failing bound variable
                        foundError = true;
                    }
                }
            CHECK_NEXT_BOUND_VARIABLE: ;  // should goto here only if the bound for the current variable has been discovered (otherwise, return with null from this method)
            }
            return foundError ? null : bounds;
        }

        /// <summary>
        /// If the return value is negative, the resulting "e0" and "e1" should not be used.
        /// Otherwise, the following is true on return:
        /// The new "e0 op e1" is equivalent to the old "e0 op e1".
        /// One of "e0" and "e1" is the identifier "boundVars[bvi]"; the return value is either 0 or 1, and indicates which.
        /// The other of "e0" and "e1" is an expression whose free variables are not among "boundVars[bvi..]".
        /// Ensures that the resulting "e0" and "e1" are not ConcreteSyntaxExpression's.
        /// </summary>
        static int SanitizeForBoundDiscovery(List<BoundVar> boundVars, int bvi, BinaryExpr.ResolvedOpcode op, ref Expression e0, ref Expression e1) {
            Contract.Requires(e0 != null);
            Contract.Requires(e1 != null);
            Contract.Requires(boundVars != null);
            Contract.Requires(0 <= bvi && bvi < boundVars.Count);
            Contract.Ensures(Contract.Result<int>() < 2);
            Contract.Ensures(!(Contract.ValueAtReturn(out e0) is ConcreteSyntaxExpression));
            Contract.Ensures(!(Contract.ValueAtReturn(out e1) is ConcreteSyntaxExpression));

            var bv = boundVars[bvi];
            e0 = e0.Resolved;
            e1 = e1.Resolved;

            // make an initial assessment of where bv is; to continue, we need bv to appear in exactly one operand
            var fv0 = FreeVariables(e0);
            var fv1 = FreeVariables(e1);
            Expression thisSide;
            Expression thatSide;
            int whereIsBv;
            if (fv0.Contains(bv)) {
                if (fv1.Contains(bv)) {
                    return -1;
                }
                whereIsBv = 0;
                thisSide = e0; thatSide = e1;
            } else if (fv1.Contains(bv)) {
                whereIsBv = 1;
                thisSide = e1; thatSide = e0;
            } else {
                return -1;
            }

            // Next, clean up the side where bv is by adjusting both sides of the expression
            switch (op) {
                case BinaryExpr.ResolvedOpcode.EqCommon:
                case BinaryExpr.ResolvedOpcode.NeqCommon:
                case BinaryExpr.ResolvedOpcode.Gt:
                case BinaryExpr.ResolvedOpcode.Ge:
                case BinaryExpr.ResolvedOpcode.Le:
                case BinaryExpr.ResolvedOpcode.Lt:
                    // Repeatedly move additive or subtractive terms from thisSide to thatSide
                    while (true) {
                        var bin = thisSide as BinaryExpr;
                        if (bin == null) {
                            break;  // done simplifying

                        } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Add) {
                            // Change "A+B op C" into either "A op C-B" or "B op C-A", depending on where we find bv among A and B.
                            if (!FreeVariables(bin.E1).Contains(bv)) {
                                thisSide = bin.E0.Resolved;
                                thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E1);
                            } else {
                                thisSide = bin.E1.Resolved;
                                thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E0);
                            }
                            ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Sub;
                            thatSide.Type = bin.Type;

                        } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub) {
                            // Change "A-B op C" in a similar way.
                            if (!FreeVariables(bin.E1).Contains(bv)) {
                                // change to "A op C+B"
                                thisSide = bin.E0.Resolved;
                                thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Add, thatSide, bin.E1);
                                ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Add;
                            } else {
                                // In principle, change to "-B op C-A" and then to "B dualOp A-C".  But since we don't want
                                // to change "op", we instead end with "A-C op B" and switch the mapping of thisSide/thatSide
                                // to e0/e1 (by inverting "whereIsBv").
                                thisSide = bin.E1.Resolved;
                                thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, bin.E0, thatSide);
                                ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Sub;
                                whereIsBv = 1 - whereIsBv;
                            }
                            thatSide.Type = bin.Type;

                        } else {
                            break;  // done simplifying
                        }
                    }
                    break;

                default:
                    break;
            }

            // Now, see if the interesting side is simply bv itself
            if (thisSide is IdentifierExpr && ((IdentifierExpr)thisSide).Var == bv) {
                // we're cool
            } else {
                // no, the situation is more complicated than we care to understand
                return -1;
            }

            // Finally, check that the other side does not contain "bv" or any of the bound variables
            // listed after "bv" in the quantifier.
            var fv = FreeVariables(thatSide);
            for (int i = bvi; i < boundVars.Count; i++) {
                if (fv.Contains(boundVars[i])) {
                    return -1;
                }
            }

            // As we return, also return the adjusted sides
            if (whereIsBv == 0) {
                e0 = thisSide; e1 = thatSide;
            } else {
                e0 = thatSide; e1 = thisSide;
            }
            return whereIsBv;
        }

        /// <summary>
        /// Returns all conjuncts of "expr" in "polarity" positions.  That is, if "polarity" is "true", then
        /// returns the conjuncts of "expr" in positive positions; else, returns the conjuncts of "expr" in
        /// negative positions.  The method considers a canonical-like form of the expression that pushes
        /// negations inwards far enough that one can determine what the result is going to be (so, almost
        /// a negation normal form).
        /// As a convenience, arithmetic inequalities are rewritten so that the negation of an arithmetic
        /// inequality is never returned and the comparisons > and >= are never returned; the negation of
        /// a common equality or disequality is rewritten analogously.
        /// Requires "expr" to be successfully resolved.
        /// Ensures that what is returned is not a ConcreteSyntaxExpression.
        /// </summary>
        static IEnumerable<Expression> NormalizedConjuncts(Expression expr, bool polarity) {
            // We consider 5 cases.  To describe them, define P(e)=Conjuncts(e,true) and N(e)=Conjuncts(e,false).
            //   *  X ==> Y    is treated as a shorthand for !X || Y, and so is described by the remaining cases
            //   *  X && Y     P(_) = P(X),P(Y)    and    N(_) = !(X && Y)
            //   *  X || Y     P(_) = (X || Y)     and    N(_) = N(X),N(Y)
            //   *  !X         P(_) = N(X)         and    N(_) = P(X)
            //   *  else       P(_) = else         and    N(_) = !else
            // So for ==>, we have:
            //   *  X ==> Y    P(_) = P(!X || Y) = (!X || Y) = (X ==> Y)
            //                 N(_) = N(!X || Y) = N(!X),N(Y) = P(X),N(Y)
            expr = expr.Resolved;

            // Binary expressions
            var b = expr as BinaryExpr;
            if (b != null) {
                bool breakDownFurther = false;
                bool p0 = polarity;
                if (b.ResolvedOp == BinaryExpr.ResolvedOpcode.And) {
                    breakDownFurther = polarity;
                } else if (b.ResolvedOp == BinaryExpr.ResolvedOpcode.Or) {
                    breakDownFurther = !polarity;
                } else if (b.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp) {
                    breakDownFurther = !polarity;
                    p0 = !p0;
                }
                if (breakDownFurther) {
                    foreach (var c in NormalizedConjuncts(b.E0, p0)) {
                        yield return c;
                    }
                    foreach (var c in NormalizedConjuncts(b.E1, polarity)) {
                        yield return c;
                    }
                    yield break;
                }
            }

            // Unary expression
            var u = expr as UnaryExpr;
            if (u != null && u.Op == UnaryExpr.Opcode.Not) {
                foreach (var c in NormalizedConjuncts(u.E, !polarity)) {
                    yield return c;
                }
                yield break;
            }

            // no other case applied, so return the expression or its negation, but first clean it up a little
            b = expr as BinaryExpr;
            if (b != null) {
                BinaryExpr.Opcode newOp;
                BinaryExpr.ResolvedOpcode newROp;
                bool swapOperands;
                switch (b.ResolvedOp) {
                    case BinaryExpr.ResolvedOpcode.Gt:  // A > B         yield polarity ? (B < A) : (A <= B);
                        newOp = polarity ? BinaryExpr.Opcode.Lt : BinaryExpr.Opcode.Le;
                        newROp = polarity ? BinaryExpr.ResolvedOpcode.Lt : BinaryExpr.ResolvedOpcode.Le;
                        swapOperands = polarity;
                        break;
                    case BinaryExpr.ResolvedOpcode.Ge:  // A >= B        yield polarity ? (B <= A) : (A < B);
                        newOp = polarity ? BinaryExpr.Opcode.Le : BinaryExpr.Opcode.Lt;
                        newROp = polarity ? BinaryExpr.ResolvedOpcode.Le : BinaryExpr.ResolvedOpcode.Lt;
                        swapOperands = polarity;
                        break;
                    case BinaryExpr.ResolvedOpcode.Le:  // A <= B        yield polarity ? (A <= B) : (B < A);
                        newOp = polarity ? BinaryExpr.Opcode.Le : BinaryExpr.Opcode.Lt;
                        newROp = polarity ? BinaryExpr.ResolvedOpcode.Le : BinaryExpr.ResolvedOpcode.Lt;
                        swapOperands = !polarity;
                        break;
                    case BinaryExpr.ResolvedOpcode.Lt:  // A < B         yield polarity ? (A < B) : (B <= A);
                        newOp = polarity ? BinaryExpr.Opcode.Lt : BinaryExpr.Opcode.Le;
                        newROp = polarity ? BinaryExpr.ResolvedOpcode.Lt : BinaryExpr.ResolvedOpcode.Le;
                        swapOperands = !polarity;
                        break;
                    case BinaryExpr.ResolvedOpcode.EqCommon:  // A == B         yield polarity ? (A == B) : (A != B);
                        newOp = polarity ? BinaryExpr.Opcode.Eq : BinaryExpr.Opcode.Neq;
                        newROp = polarity ? BinaryExpr.ResolvedOpcode.EqCommon : BinaryExpr.ResolvedOpcode.NeqCommon;
                        swapOperands = false;
                        break;
                    case BinaryExpr.ResolvedOpcode.NeqCommon:  // A != B         yield polarity ? (A != B) : (A == B);
                        newOp = polarity ? BinaryExpr.Opcode.Neq : BinaryExpr.Opcode.Eq;
                        newROp = polarity ? BinaryExpr.ResolvedOpcode.NeqCommon : BinaryExpr.ResolvedOpcode.EqCommon;
                        swapOperands = false;
                        break;
                    default:
                        goto JUST_RETURN_IT;
                }
                if (newROp != b.ResolvedOp || swapOperands) {
                    b = new BinaryExpr(b.tok, newOp, swapOperands ? b.E1 : b.E0, swapOperands ? b.E0 : b.E1);
                    b.ResolvedOp = newROp;
                    b.Type = Type.Bool;
                    yield return b;
                    yield break;
                }
            }
        JUST_RETURN_IT: ;
            if (polarity) {
                yield return expr;
            } else {
                expr = new UnaryExpr(expr.tok, UnaryExpr.Opcode.Not, expr);
                expr.Type = Type.Bool;
                yield return expr;
            }
        }

        public static Expression Plus(Expression e, int n) {
            Contract.Requires(0 <= n);

            var nn = new LiteralExpr(e.tok, n);
            nn.Type = Type.Int;
            var p = new BinaryExpr(e.tok, BinaryExpr.Opcode.Add, e, nn);
            p.ResolvedOp = BinaryExpr.ResolvedOpcode.Add;
            p.Type = Type.Int;
            return p;
        }

        public static Expression Minus(Expression e, int n) {
            Contract.Requires(0 <= n);

            var nn = new LiteralExpr(e.tok, n);
            nn.Type = Type.Int;
            var p = new BinaryExpr(e.tok, BinaryExpr.Opcode.Sub, e, nn);
            p.ResolvedOp = BinaryExpr.ResolvedOpcode.Sub;
            p.Type = Type.Int;
            return p;
        }

        /// <summary>
        /// Returns the set of free variables in "expr".
        /// Requires "expr" to be successfully resolved.
        /// Ensures that the set returned has no aliases.
        /// </summary>
        static ISet<IVariable> FreeVariables(Expression expr) {
            Contract.Requires(expr != null);
            Contract.Ensures(expr.Type != null);

            if (expr is IdentifierExpr) {
                var e = (IdentifierExpr)expr;
                return new HashSet<IVariable>() { e.Var };

            } else if (expr is QuantifierExpr) {
                var e = (QuantifierExpr)expr;
                var s = FreeVariables(e.LogicalBody());
                foreach (var bv in e.BoundVars) {
                    s.Remove(bv);
                }
                return s;

            } else if (expr is MatchExpr) {
                var e = (MatchExpr)expr;
                var s = FreeVariables(e.Source);
                foreach (MatchCaseExpr mc in e.Cases) {
                    var t = FreeVariables(mc.Body);
                    foreach (var bv in mc.Arguments) {
                        t.Remove(bv);
                    }
                    s.UnionWith(t);
                }
                return s;

            } else {
                ISet<IVariable> s = null;
                foreach (var e in expr.SubExpressions) {
                    var t = FreeVariables(e);
                    if (s == null) {
                        s = t;
                    } else {
                        s.UnionWith(t);
                    }
                }
                return s == null ? new HashSet<IVariable>() : s;
            }
        }

        void ResolveReceiver(Expression expr, bool twoState) {
            Contract.Requires(expr != null);
            Contract.Ensures(expr.Type != null);

            if (expr is ThisExpr && !expr.WasResolved()) {
                // Allow 'this' here, regardless of scope.AllowInstance.  The caller is responsible for
                // making sure 'this' does not really get used when it's not available.
                Contract.Assume(currentClass != null);  // this is really a precondition, in this case
                expr.Type = GetThisType(expr.tok, currentClass);
            } else {
                ResolveExpression(expr, twoState);
            }
        }

        void ResolveSeqSelectExpr(SeqSelectExpr e, bool twoState, bool allowNonUnitArraySelection) {
            Contract.Requires(e != null);
            if (e.Type != null) {
                // already resolved
                return;
            }

            bool seqErr = false;
            ResolveExpression(e.Seq, twoState);
            Contract.Assert(e.Seq.Type != null);  // follows from postcondition of ResolveExpression
            Type elementType = new InferredTypeProxy();
            Type domainType = new InferredTypeProxy();

            IndexableTypeProxy expectedType = new IndexableTypeProxy(elementType, domainType);
            if (!UnifyTypes(e.Seq.Type, expectedType)) {
                Error(e, "sequence/array/map selection requires a sequence, array or map (got {0})", e.Seq.Type);
                seqErr = true;
            }
            if (!e.SelectOne)  // require sequence or array
      {
                if (!allowNonUnitArraySelection) {
                    // require seq
                    if (!UnifyTypes(expectedType, new SeqType(new InferredTypeProxy()))) {
                        Error(e, "selection requires a sequence (got {0})", e.Seq.Type);
                    }
                } else {
                    if (UnifyTypes(expectedType, new MapType(new InferredTypeProxy(), new InferredTypeProxy()))) {
                        Error(e, "cannot multiselect a map (got {0} as map type)", e.Seq.Type);
                    }
                }
            }
            if (e.E0 != null) {
                ResolveExpression(e.E0, twoState);
                Contract.Assert(e.E0.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E0.Type, domainType)) {
                    Error(e.E0, "sequence/array/map selection requires {1} indices (got {0})", e.E0.Type, domainType);
                }
            }
            if (e.E1 != null) {
                ResolveExpression(e.E1, twoState);
                Contract.Assert(e.E1.Type != null);  // follows from postcondition of ResolveExpression
                if (!UnifyTypes(e.E1.Type, domainType)) {
                    Error(e.E1, "sequence/array/map selection requires {1} indices (got {0})", e.E1.Type, domainType);
                }
            }
            if (!seqErr) {
                if (e.SelectOne) {
                    e.Type = elementType;
                } else {
                    e.Type = new SeqType(elementType);
                }
            }
        }

        // Yuyan
        UnaryExpr.ResolvedOpcode ResolveOp(UnaryExpr.Opcode op, Type operandType) {
            Contract.Requires(operandType != null);
            switch (op) {
                case UnaryExpr.Opcode.Length:
                    if (operandType is SeqType) {
                        return UnaryExpr.ResolvedOpcode.SeqLength;
                    } else {
                        return UnaryExpr.ResolvedOpcode.RegionLength;
                    }
                case UnaryExpr.Opcode.Not:
                    return UnaryExpr.ResolvedOpcode.Not;
                case UnaryExpr.Opcode.SetChoose:
                    return UnaryExpr.ResolvedOpcode.SetChoose;
                default:
                    Contract.Assert(false); throw new cce.UnreachableException();  // unexpected operator
            }
        }

        /// <summary>
        /// Note: this method is allowed to be called even if "type" does not make sense for "op", as might be the case if
        /// resolution of the binary expression failed.  If so, an arbitrary resolved opcode is returned.
        /// </summary>
        BinaryExpr.ResolvedOpcode ResolveOp(BinaryExpr.Opcode op, Type operandType) {
            Contract.Requires(operandType != null);
            switch (op) {
                case BinaryExpr.Opcode.Iff: return BinaryExpr.ResolvedOpcode.Iff;
                case BinaryExpr.Opcode.Imp: return BinaryExpr.ResolvedOpcode.Imp;
                case BinaryExpr.Opcode.And: return BinaryExpr.ResolvedOpcode.And;
                case BinaryExpr.Opcode.Or: return BinaryExpr.ResolvedOpcode.Or;
                case BinaryExpr.Opcode.Eq:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.SetEq;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSetEq;
                    } else if (operandType is SeqType) {
                        return BinaryExpr.ResolvedOpcode.SeqEq;
                    } else if (operandType is MapType) {
                        return BinaryExpr.ResolvedOpcode.MapEq;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionEq;
                    } else {
                        return BinaryExpr.ResolvedOpcode.EqCommon;
                    }
                case BinaryExpr.Opcode.Neq:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.SetNeq;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSetNeq;
                    } else if (operandType is SeqType) {
                        return BinaryExpr.ResolvedOpcode.SeqNeq;
                    } else if (operandType is MapType) {
                        return BinaryExpr.ResolvedOpcode.MapNeq;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionNeq;
                    } else {
                        return BinaryExpr.ResolvedOpcode.NeqCommon;
                    }
                case BinaryExpr.Opcode.Disjoint:
                    if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSetDisjoint;
                    } else if (operandType is MapType) {
                        return BinaryExpr.ResolvedOpcode.MapDisjoint;
                    } if (operandType is RegionType) {   // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionDisjoint;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Disjoint;
                    }
                case BinaryExpr.Opcode.Lt:
                    if (operandType.IsIndDatatype || operandType is DatatypeProxy) {
                        return BinaryExpr.ResolvedOpcode.RankLt;
                    } else if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.ProperSubset;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.ProperMultiSubset;
                    } else if (operandType is SeqType) {
                        return BinaryExpr.ResolvedOpcode.ProperPrefix;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionSubRegion;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Lt;
                    }
                case BinaryExpr.Opcode.Le:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.Subset;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSubset;
                    } else if (operandType is SeqType) {
                        return BinaryExpr.ResolvedOpcode.Prefix;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionSubOrEqualRegion;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Le;
                    }
                case BinaryExpr.Opcode.Add:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.Union;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSetUnion;
                    } else if (operandType is MapType) {
                        return BinaryExpr.ResolvedOpcode.MapUnion;
                    } else if (operandType is SeqType) {
                        return BinaryExpr.ResolvedOpcode.Concat;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionUnion;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Add;
                    }
                case BinaryExpr.Opcode.Sub:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.SetDifference;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSetDifference;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionDifference;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Sub;
                    }
                case BinaryExpr.Opcode.Mul:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.Intersection;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSetIntersection;
                    } else if (operandType is RegionType) {   // Yuyan
                        return BinaryExpr.ResolvedOpcode.RegionIntersection;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Mul;
                    }
                case BinaryExpr.Opcode.Gt:
                    if (operandType.IsDatatype) {
                        return BinaryExpr.ResolvedOpcode.RankGt;
                    } else if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.ProperSuperset;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.ProperMultiSuperset;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Gt;
                    }
                case BinaryExpr.Opcode.Ge:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.Superset;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.MultiSuperset;
                    } else {
                        return BinaryExpr.ResolvedOpcode.Ge;
                    }
                case BinaryExpr.Opcode.In:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.InSet;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.InMultiSet;
                    } else if (operandType is MapType) {
                        return BinaryExpr.ResolvedOpcode.InMap;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.InRegion;
                    } else {
                        return BinaryExpr.ResolvedOpcode.InSeq;
                    }
                case BinaryExpr.Opcode.NotIn:
                    if (operandType is SetType) {
                        return BinaryExpr.ResolvedOpcode.NotInSet;
                    } else if (operandType is MultiSetType) {
                        return BinaryExpr.ResolvedOpcode.NotInMultiSet;
                    } else if (operandType is MapType) {
                        return BinaryExpr.ResolvedOpcode.NotInMap;
                    } else if (operandType is RegionType) { // Yuyan
                        return BinaryExpr.ResolvedOpcode.NotInRegion;
                    } else {
                        return BinaryExpr.ResolvedOpcode.NotInSeq;
                    }
                case BinaryExpr.Opcode.Div: return BinaryExpr.ResolvedOpcode.Div;
                case BinaryExpr.Opcode.Mod: return BinaryExpr.ResolvedOpcode.Mod;
                default:
                    Contract.Assert(false); throw new cce.UnreachableException();  // unexpected operator
            }
        }

        /// <summary>
        /// Returns whether or not 'expr' has any subexpression that uses some feature (like a ghost or quantifier)
        /// that is allowed only in specification contexts.
        /// Requires 'expr' to be a successfully resolved expression.
        /// </summary>
        bool UsesSpecFeatures(Expression expr) {
            Contract.Requires(expr != null);
            Contract.Requires(expr.WasResolved());  // this check approximates the requirement that "expr" be resolved

            if (expr is LiteralExpr) {
                return false;
            } else if (expr is ThisExpr) {
                return false;
            } else if (expr is IdentifierExpr) {
                IdentifierExpr e = (IdentifierExpr)expr;
                return cce.NonNull(e.Var).IsGhost;
            } else if (expr is DatatypeValue) {
                DatatypeValue dtv = (DatatypeValue)expr;
                return dtv.Arguments.Exists(arg => UsesSpecFeatures(arg));
            } else if (expr is DisplayExpression) {
                DisplayExpression e = (DisplayExpression)expr;
                return e.Elements.Exists(ee => UsesSpecFeatures(ee));
            } else if (expr is MapDisplayExpr) {
                MapDisplayExpr e = (MapDisplayExpr)expr;
                return e.Elements.Exists(p => UsesSpecFeatures(p.A) || UsesSpecFeatures(p.B));
            } else if (expr is FieldSelectExpr) {
                FieldSelectExpr e = (FieldSelectExpr)expr;
                return cce.NonNull(e.Field).IsGhost || UsesSpecFeatures(e.Obj);
            } else if (expr is SeqSelectExpr) {
                SeqSelectExpr e = (SeqSelectExpr)expr;
                return UsesSpecFeatures(e.Seq) ||
                       (e.E0 != null && UsesSpecFeatures(e.E0)) ||
                       (e.E1 != null && UsesSpecFeatures(e.E1));
            } else if (expr is MultiSelectExpr) {
                MultiSelectExpr e = (MultiSelectExpr)expr;
                return UsesSpecFeatures(e.Array) || e.Indices.Exists(ee => UsesSpecFeatures(ee));
            } else if (expr is SeqUpdateExpr) {
                SeqUpdateExpr e = (SeqUpdateExpr)expr;
                return UsesSpecFeatures(e.Seq) ||
                       (e.Index != null && UsesSpecFeatures(e.Index)) ||
                       (e.Value != null && UsesSpecFeatures(e.Value));
            } else if (expr is FunctionCallExpr) {
                FunctionCallExpr e = (FunctionCallExpr)expr;
                if (cce.NonNull(e.Function).IsGhost) {
                    return true;
                }
                return e.Args.Exists(arg => UsesSpecFeatures(arg));
            } else if (expr is OldExpr) {
                OldExpr e = (OldExpr)expr;
                return UsesSpecFeatures(e.E);
            } else if (expr is FreshExpr) {
                return true;
            } else if (expr is UnaryExpr) {
                UnaryExpr e = (UnaryExpr)expr;
                return UsesSpecFeatures(e.E);
            } else if (expr is BinaryExpr) {
                BinaryExpr e = (BinaryExpr)expr;
                if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.RankLt || e.ResolvedOp == BinaryExpr.ResolvedOpcode.RankGt) {
                    return true;
                }
                return UsesSpecFeatures(e.E0) || UsesSpecFeatures(e.E1);
            } else if (expr is NamedExpr) {
                return moduleInfo.IsGhost ? false : UsesSpecFeatures(((NamedExpr)expr).Body);
            } else if (expr is ComprehensionExpr) {
                if (expr is QuantifierExpr && ((QuantifierExpr)expr).Bounds == null) {
                    return true;  // the quantifier cannot be compiled if the resolver found no bounds
                }
                return Contract.Exists(expr.SubExpressions, se => UsesSpecFeatures(se));
            } else if (expr is SetComprehension) {
                var e = (SetComprehension)expr;
                return (e.Range != null && UsesSpecFeatures(e.Range)) || (e.Term != null && UsesSpecFeatures(e.Term));
            } else if (expr is MapComprehension) {
                var e = (MapComprehension)expr;
                return (UsesSpecFeatures(e.Range)) || (UsesSpecFeatures(e.Term));
            } else if (expr is WildcardExpr) {
                return false;
            } else if (expr is PredicateExpr) {
                var e = (PredicateExpr)expr;
                return UsesSpecFeatures(e.Body);
            } else if (expr is ITEExpr) {
                ITEExpr e = (ITEExpr)expr;
                return UsesSpecFeatures(e.Test) || UsesSpecFeatures(e.Thn) || UsesSpecFeatures(e.Els);
            } else if (expr is MatchExpr) {
                MatchExpr me = (MatchExpr)expr;
                if (UsesSpecFeatures(me.Source)) {
                    return true;
                }
                return me.Cases.Exists(mc => UsesSpecFeatures(mc.Body));
            } else if (expr is ConcreteSyntaxExpression) {
                var e = (ConcreteSyntaxExpression)expr;
                return e.ResolvedExpression != null && UsesSpecFeatures(e.ResolvedExpression);
            } else {
                Contract.Assert(false); throw new cce.UnreachableException();  // unexpected expression
            }
        }
    }

    class CoCallResolution {
        readonly ModuleDefinition currentModule;
        readonly Function currentFunction;
        readonly bool dealsWithCodatatypes;

        public CoCallResolution(Function currentFunction, bool dealsWithCodatatypes) {
            Contract.Requires(currentFunction != null);
            this.currentModule = currentFunction.EnclosingClass.Module;
            this.currentFunction = currentFunction;
            this.dealsWithCodatatypes = dealsWithCodatatypes;
        }

        /// <summary>
        /// Determines which calls in "expr" can be considered to be co-calls, which co-constructor
        /// invocations host such co-calls, and which destructor operations are not allowed.
        /// Assumes "expr" to have been successfully resolved.
        /// </summary>
        public void CheckCoCalls(Expression expr) {
            Contract.Requires(expr != null);
            var candidates = CheckCoCalls(expr, true, null);
            ProcessCoCallInfo(candidates);
        }

        struct CoCallInfo {
            public int GuardCount;
            public FunctionCallExpr CandidateCall;
            public DatatypeValue EnclosingCoConstructor;
            public CoCallInfo(int guardCount, FunctionCallExpr candidateCall, DatatypeValue enclosingCoConstructor) {
                Contract.Requires(0 <= guardCount);
                Contract.Requires(candidateCall != null);

                GuardCount = guardCount;
                CandidateCall = candidateCall;
                EnclosingCoConstructor = enclosingCoConstructor;
            }

            public void AdjustGuardCount(int p) {
                GuardCount += p;
            }
        }

        /// <summary>
        /// Recursively goes through the entire "expr".  Every call within the same recursive cluster is a potential
        /// co-call.  All such calls will get their .CoCall field filled in, indicating whether or not the call
        /// is a co-call.  If the situation deals with co-datatypes, then one of the NoBecause... values is chosen (rather
        /// than just No), so that any error message that may later be produced when trying to prove termination of the
        /// recursive call can include a note pointing out that the call was not selected to be a co-call.
        /// "allowCallsWithinRecursiveCluster" is passed in as "false" if the enclosing context has no easy way of
        /// controlling the uses of "expr" (for example, if the enclosing context passes "expr" to a function or binds
        /// "expr" to a variable).
        /// </summary>
        List<CoCallInfo> CheckCoCalls(Expression expr, bool allowCallsWithinRecursiveCluster, DatatypeValue coContext) {
            Contract.Requires(expr != null);
            Contract.Ensures(allowCallsWithinRecursiveCluster || Contract.Result<List<CoCallInfo>>().Count == 0);

            var candidates = new List<CoCallInfo>();
            expr = expr.Resolved;
            if (expr is DatatypeValue) {
                var e = (DatatypeValue)expr;
                if (e.Ctor.EnclosingDatatype is CoDatatypeDecl) {
                    foreach (var arg in e.Arguments) {
                        foreach (var c in CheckCoCalls(arg, allowCallsWithinRecursiveCluster, e)) {
                            c.AdjustGuardCount(1);
                            candidates.Add(c);
                        }
                    }
                    return candidates;
                }
            } else if (expr is FieldSelectExpr) {
                var e = (FieldSelectExpr)expr;
                if (e.Field.EnclosingClass is CoDatatypeDecl) {
                    foreach (var c in CheckCoCalls(e.Obj, allowCallsWithinRecursiveCluster, null)) {
                        if (c.GuardCount <= 1) {
                            // This call was not guarded (c.GuardedCount == 0) or the guard for this candidate co-call is
                            // being removed (c.GuardedCount == 1), so this call is not allowed as a co-call.
                            // (So, don't include "res" among "results".)
                            c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded;
                        } else {
                            c.AdjustGuardCount(-1);
                            candidates.Add(c);
                        }
                    }
                    return candidates;
                }
            } else if (expr is MatchExpr) {
                var e = (MatchExpr)expr;
                var r = CheckCoCalls(e.Source, false, null);
                Contract.Assert(r.Count == 0);  // follows from postcondition of CheckCoCalls
                foreach (var kase in e.Cases) {
                    r = CheckCoCalls(kase.Body, allowCallsWithinRecursiveCluster, null);
                    candidates.AddRange(r);
                }
                return candidates;
            } else if (expr is FunctionCallExpr) {
                var e = (FunctionCallExpr)expr;
                // First, consider the arguments of the call, making sure that they do not include calls within the recursive cluster.
                // (Note, if functions could have a "destruction level" declaration that promised to only destruct its arguments by
                // so much, then some recursive calls within the cluster could be allowed.)
                foreach (var arg in e.Args) {
                    var r = CheckCoCalls(arg, false, null);
                    Contract.Assert(r.Count == 0);  // follows from postcondition of CheckCoCalls
                }
                // Second, investigate the possibility that this call itself may be a candidate co-call
                if (currentModule.CallGraph.GetSCCRepresentative(currentFunction) == currentModule.CallGraph.GetSCCRepresentative(e.Function)) {
                    // This call goes to another function in the same recursive cluster
                    if (e.Function.Reads.Count != 0) {
                        // this call is disqualified from being a co-call, because of side effects
                        if (!dealsWithCodatatypes) {
                            e.CoCall = FunctionCallExpr.CoCallResolution.No;
                        } else if (coContext != null) {
                            e.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects;
                        } else {
                            e.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded;
                        }
                    } else if (!allowCallsWithinRecursiveCluster) {
                        if (!dealsWithCodatatypes) {
                            e.CoCall = FunctionCallExpr.CoCallResolution.No;
                        } else {
                            e.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext;
                        }
                    } else {
                        // e.CoCall is not filled in here, but will be filled in when the list of candidates are processed
                        candidates.Add(new CoCallInfo(0, e, coContext));
                    }
                }
                return candidates;
            } else if (expr is OldExpr) {
                var e = (OldExpr)expr;
                // here, "coContext" is passed along (the use of "old" says this must be ghost code, so the compiler does not need to handle this case)
                return CheckCoCalls(e.E, allowCallsWithinRecursiveCluster, coContext);
            } else if (expr is LetExpr) {
                var e = (LetExpr)expr;
                foreach (var rhs in e.RHSs) {
                    var r = CheckCoCalls(rhs, false, null);
                    Contract.Assert(r.Count == 0);  // follows from postcondition of CheckCoCalls
                }
                return CheckCoCalls(e.Body, allowCallsWithinRecursiveCluster, null);
            } else if (expr is ComprehensionExpr) {
                var e = (ComprehensionExpr)expr;
                foreach (var ee in e.SubExpressions) {
                    var r = CheckCoCalls(ee, false, null);
                    Contract.Assert(r.Count == 0);  // follows from postcondition of CheckCoCalls
                }
                return candidates;
            }

            // Default handling:
            foreach (var ee in expr.SubExpressions) {
                var r = CheckCoCalls(ee, allowCallsWithinRecursiveCluster, null);
                candidates.AddRange(r);
            }
            if (expr.Type is BasicType) {
                // nothing can escape this expression, so process the results now
                ProcessCoCallInfo(candidates);
                candidates.Clear();
            }
            return candidates;
        }

        /// <summary>
        /// This method is to be called when it has been determined that all candidate
        /// co-calls in "info" are indeed allowed as co-calls.
        /// </summary>
        void ProcessCoCallInfo(List<CoCallInfo> coCandidates) {
            foreach (var c in coCandidates) {
                if (c.GuardCount != 0) {
                    c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.Yes;
                    c.EnclosingCoConstructor.IsCoCall = true;
                } else if (dealsWithCodatatypes) {
                    c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded;
                } else {
                    c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.No;
                }
            }
        }
    }

    class Scope<Thing> where Thing : class {
        [Rep]
        readonly List<string> names = new List<string>();  // a null means a marker
        [Rep]
        readonly List<Thing> things = new List<Thing>();
        [ContractInvariantMethod]
        void ObjectInvariant() {
            Contract.Invariant(names != null);
            Contract.Invariant(things != null);
            Contract.Invariant(names.Count == things.Count);
            Contract.Invariant(-1 <= scopeSizeWhereInstancesWereDisallowed && scopeSizeWhereInstancesWereDisallowed <= names.Count);
        }

        int scopeSizeWhereInstancesWereDisallowed = -1;

        public bool AllowInstance {
            get { return scopeSizeWhereInstancesWereDisallowed == -1; }
            set {
                Contract.Requires(AllowInstance && !value);  // only allowed to change from true to false (that's all that's currently needed in Dafny); Pop is what can make the change in the other direction
                scopeSizeWhereInstancesWereDisallowed = names.Count;
            }
        }

        public void PushMarker() {
            names.Add(null);
            things.Add(null);
        }

        public void PopMarker() {
            int n = names.Count;
            while (true) {
                n--;
                if (names[n] == null) {
                    break;
                }
            }
            names.RemoveRange(n, names.Count - n);
            things.RemoveRange(n, things.Count - n);
            if (names.Count < scopeSizeWhereInstancesWereDisallowed) {
                scopeSizeWhereInstancesWereDisallowed = -1;
            }
        }

        // Pushes name-->thing association and returns "true", if name has not already been pushed since the last marker.
        // If name already has been pushed since the last marker, does nothing and returns "false".
        public bool Push(string name, Thing thing) {
            Contract.Requires(name != null);
            Contract.Requires(thing != null);
            if (Find(name, true) != null) {
                return false;
            } else {
                names.Add(name);
                things.Add(thing);
                return true;
            }
        }

        Thing Find(string name, bool topScopeOnly) {
            Contract.Requires(name != null);
            for (int n = names.Count; 0 <= --n; ) {
                if (names[n] == null) {
                    if (topScopeOnly) {
                        return null;  // no present
                    }
                } else if (names[n] == name) {
                    Thing t = things[n];
                    Contract.Assert(t != null);
                    return t;
                }
            }
            return null;  // not present
        }

        public Thing Find(string name) {
            Contract.Requires(name != null);
            return Find(name, false);
        }

        public bool ContainsDecl(Thing t) {
            return things.Exists(thing => thing == t);
        }
    }
}
